-
Notifications
You must be signed in to change notification settings - Fork 20
/
cf2019.asm
6820 lines (5915 loc) · 218 KB
/
cf2019.asm
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
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; cf2019.nasm 2019 Apr 04 "drabbest-humblest" (512K byte image size) with arrow, Enter and Escape keys
; colorForth for 80x86 PC for NASM , with 1024x768 and 800x600 graphics options
; Adapted by Howerd Oakford from code by :
; Chuck Moore : inventor, MASM
; Mark Slicker : ported to GNU Assembler
; Peter Appelman : ported to NASM with qwerty keyboard
; John Comeau : BIOS boot from ClusterFix
; and others... Thanks to all!!!
; Feedback welcome : [email protected] www.inventio.co.uk
; %define NOT_BOCHS Bochs cannot handle resetting of the PIT chips, so we can optionally disable this
; CPU 386 ; Assemble instructions for the 386 instruction set
%define FORCE_800x600_VESA 0 ; true to force 800 x 600 x 16 bits for testing in bochs
%define START_BLOCK_NUMBER 32 ; must be an even number
%define SIZE_OF_FONT_IN_BLOCKS 6
%define OFFSET_OF_FONT ( ( START_BLOCK_NUMBER - SIZE_OF_FONT_IN_BLOCKS ) * 0x400 )
%define LAST_BLOCK_NUMBER 511 ; must be an odd number
%define SECTORS_TO_LOAD ( ( LAST_BLOCK_NUMBER + 1 ) * 2 ) ; number of 512 octet sectors
%define BITS_PER_PIXEL 16 ; MUST BE 16 !!! display pixel sizes, colour depth = 16 bit ( 2 bytes )
; for the maximum supported screen : 1024 x 768 pixels :
%define MAX_SCREEN_WIDTH ( 1024 ) ; maximum screen width in pixels
%define MAX_SCREEN_HEIGHT ( 768 ) ; maximum screen height in pixels
%define BYTES_PER_PIXEL ( BITS_PER_PIXEL / 8 )
PIXEL_SHIFT equ 1 ; how many bits to shift to scale by BYTES_PER_PIXEL
; Memory Map
; start length
; 0x100000 .... RAM
; 0xC0000 0xFFFFF BIOS video ROM - its not RAM!
; 0xB8000 0x08000 BIOS video RAM
; 0x10000 0xA8000 COLORFTH.CFX file is copied here
; 0x0F000 0x01000 BIOS shadow RAM - its OK to use this if we do not call the video BIOS
; 0x0A000 0x05000 BIOS video RAM - do not use until we have changed video mode
; 0x07c00 0x00200 BPB Boot sector after loading by BIOS
; 0x07c0b <----- di points here, the BPB ( + offset ) and variables ( - offset ) are accessed via [di]
; 0x07b8c 0x00080 variables referenced via [di], followed by BPB variables referenced via [di]
; 0x07800 Stacks, size = 0x0200 each , growing downwards
; 0x02000 0x06800 SECTOR_BUFFER
; 0x00000 0x02000 BIOS RAM
%define SECTOR_BUFFER 0x00002000 ; buffer for disk reads and writes
%define SECTOR_BUFFER_SIZE 0x4800 ; 18 K bytes, 36 x 512 byte sectors
%define INTERRUPT_VECTORS ( SECTOR_BUFFER - 0x0400 ) ; the IDT register points to these interrupt vectors
%define VESA_BUFFER ( INTERRUPT_VECTORS - 0x0400 ) ; for the VESA mode information
%define DAP_BUFFER ( VESA_BUFFER - 0x0020 ) ; 0x1BE0 for the Int 0x13 Disk Address Packet (DAP)
%define DISK_INFO ( DAP_BUFFER - 0x0020 ) ; for the Int 0x13 AH=08h get info
%define IDT_AND_PIC_SETTINGS ( DISK_INFO - 0x0040 ) ; bytes 0x00 - 0x05 SIDT value, 0x06 PIC1 IMR , 0x07 PIC2 IMR values saved at startup
%define V_REGS ( IDT_AND_PIC_SETTINGS - 0x0020 ) ; test only - registers before and after thunk call
%define TRASH_BUFFER ( V_REGS - 0x0400 ) ; saves words deleted while editing
%define PIC_BIOS_IDT_SETTINGS ( IDT_AND_PIC_SETTINGS ) ; bytes 0x00 - 0x05 SIDT value, 0x06 PIC1 IMR , 0x07 PIC2 IMR values saved at startup
%define PIC_BIOS_IMR_SETTINGS ( IDT_AND_PIC_SETTINGS + 6 ) ; bytes 0x00 - 0x05 SIDT value, 0x06 PIC1 IMR , 0x07 PIC2 IMR
%define PIC_NEW_IDT_SETTINGS ( IDT_AND_PIC_SETTINGS + 0x10 ) ; bytes 0x00 - 0x05 SIDT value, 0x08 new PIC1 IMR , 0x09 new PIC2 IMR
%define PIC_NEW_IMR_SETTINGS ( IDT_AND_PIC_SETTINGS + 0x16 ) ; bytes 0x00 - 0x05 SIDT value, 0x08 new PIC1 IMR , 0x09 new PIC2 IMR
%define IDT_AND_PIC_SETTINGS_PAD ( IDT_AND_PIC_SETTINGS + 0x20 )
%define vesa_BytesPerScanLine ( VESA_BUFFER + 0x0E ) ; screen width ( number of horizontal pixels )
%define vesa_XResolution ( VESA_BUFFER + 0x12 ) ; screen width ( number of horizontal pixels )
%define vesa_YResolution ( VESA_BUFFER + 0x14 ) ; screen height ( number of vertical pixels )
%define vesa_BitsPerPixel ( VESA_BUFFER + 0x19 ) ; bits per pixel
%define vesa_SavedMode ( VESA_BUFFER + 0x1E ) ; "Reserved" - we save the VESA mode here
%define vesa_PhysBasePtr ( VESA_BUFFER + 0x28 ) ; address of linear frame buffer
%define BOOTOFFSET 0x7C00
%assign RELOC_BIT 16 ; the relocation address must be a power of 2
%assign RELOCATED 1 << RELOC_BIT ; 0x10000
; stack allocation, three pairs of data and return stacks
; Note : the return stack must be in the lowest 64K byte segment, for the BIOS calls to work.
%define RETURN_STACK_0 0x7800 ; top of stack memory area ; 0xa0000 in CM's code, 0x10000 in JC's code
%define DATA_STACK_SIZE 0x0200
%define RETURN_STACK_SIZE 0x0200
; combined stack sizes
%define STACK_SIZE ( DATA_STACK_SIZE + RETURN_STACK_SIZE )
%define TWOxSTACK_SIZE ( STACK_SIZE * 2 )
%define TOTAL_STACK_SIZE ( STACK_SIZE * 3 ) ; three pairs of stacks, one for each task
%define STACK_MEMORY_START ( RETURN_STACK_0 - TOTAL_STACK_SIZE )
; data stacks
%define DATA_STACK_0 ( RETURN_STACK_0 - RETURN_STACK_SIZE ) ; 0x9f400 in CM's code
%define DATA_STACK_1 ( DATA_STACK_0 - STACK_SIZE )
%define DATA_STACK_2 ( DATA_STACK_0 - TWOxSTACK_SIZE )
; return stacks
%define RETURN_STACK_1 ( RETURN_STACK_0 - STACK_SIZE )
%define RETURN_STACK_2 ( RETURN_STACK_0 - TWOxSTACK_SIZE )
%define _TOS_ eax
%define _TOS_x_ ax
%define _TOS_l_ al
%define _SCRATCH_ ebx
%define _SCRATCH_x_ bx
%define _SCRATCH_l_ bl
%define _MOV_TOS_LIT_ (0xB8) ; the opcode for mov eax, 32_bit_literal (in next 32 bit cell)
%macro _DUP_ 0 ; Top Of Stack is in the _TOS_ register
sub esi, byte 0x04 ; lea esi, [ esi - 0x04 ] ; pre-decrement the stack pointer
mov [ esi ], _TOS_ ; copy the Top Of Stack ( TOS ) register to Second On Stack ( on the real stack )
%endmacro
%macro _SWAP_ 0
xchg _TOS_, [ esi ]
%endmacro
%macro _OVER_ 0
sub esi, byte 0x04 ; lea esi, [ esi - 0x04 ] ; pre-decrement the stack pointer
mov [ esi ], _TOS_ ; copy the Top Of Stack ( TOS ) register to Second On Stack ( on the real stack )
mov _TOS_, [ esi + 4 ]
%endmacro
%macro _DROP_ 0
lodsd
%endmacro
%define START_OF_RAM 0x00468000
%define ForthNames START_OF_RAM ; copied to RAM here from ROM ( i.e. boot program ) version
%define ForthJumpTable ( ForthNames + 0x2800 ) ; copied to RAM here from ROM ( i.e. boot program ) version
%define MacroNames ( ForthJumpTable + 0x2800 ) ; copied to RAM here from ROM ( i.e. boot program ) version
%define MacroJumpTable ( MacroNames + 0x2800 ) ; copied to RAM here from ROM ( i.e. boot program ) version
%define H0 ( MacroJumpTable + 0x2800 ) ; initial value of the dictionary pointer
%define SECTOR 512 ; bytes per floppy sector
%define HEADS 2 ; heads on 1.44M floppy drive
%define SECTORS 18 ; floppy sectors per track
%define CYLINDER (SECTOR * SECTORS * HEADS)
%define CELL 4 ; bytes per cell
%define DEBUGGER 0xe1 ; port to hardware debugger?
; int 0x13 Disk Address Packet (DAP) pointed to by si :
%define o_Int13_DAP_size ( 0x00 ) ; 2 0x0010
%define o_Int13_DAP_num_sectors ( 0x02 ) ; 2 0x0001
%define o_Int13_DAP_address ( 0x04 ) ; 2 0x2000
%define o_Int13_DAP_segment ( 0x06 ) ; 2 0x0000
%define o_Int13_DAP_LBA_64_lo ( 0x08 ) ; 4 0x00000028
%define o_Int13_DAP_LBA_64_hi ( 0x0C ) ; 4 0x00000000
; extended DAP values
%define o_Int13_DAP_readwrite ( 0x10 ) ; 2 0x0000
%define o_Int13_DAP_saved_DX ( 0x12 ) ; 2 0x0000
%define o_Int13_DAP_returned_AX ( 0x14 ) ; 2 0xHH00 see AH Return Code below
%define o_Int13_DAP_returned_carry_flag ( 0x16 ) ; 2 0x0000
%define o_Int13_DAP_saved_CHS_CX ( 0x18 ) ; 2 0x0000
%define o_Int13_DAP_saved_CHS_DX ( 0x1A ) ; 2 0x0000
%macro LOAD_RELATIVE_ADDRESS 1
mov _TOS_, ( ( ( %1 - $$ ) + RELOCATED ) )
%endmacro
; emit the given following character
%macro EMIT_IMM 1
; push esi
_DUP_
mov _TOS_, %1
call emit_
; pop esi
%endmacro
; *****************************************************************************
; Registers used
; *****************************************************************************
; _TOS_ is the top stack item ( eax --> ebx )
; esp the call ... ret return stack pointer
; edi dictionary pointer ( H --> : HERE ( -- a ) H @ ; )
; esi is the stack pointer, also needed by lods and movs
; e.g. lodsd loads a 32 bit dword from [ds:esi] into _TOS_, increments esi by 4
; ebx scratch register
; ecx counter and scratch register
; edx run-time pointer (?), "a register" used by a! , otherwise scratch register
; ebp variable pointer register
; "ds" = selector 0x10 ==> 0x0000:0000
; "es" = selector 0x10 ==> 0x0000:0000
; "ss" = selector 0x10 ==> 0x0000:0000
; colours RGB in 16 bits
colour_background equ 0x0000
colour_yellow equ 0xFFE0
colour_black equ 0x0000
colour_red equ 0xF800
colour_green equ 0x0600
colour_cyan equ 0x07FF
colour_white equ 0xFFFF
colour_light_blue equ 0x841F
colour_silver equ 0xC618
colour_magenta equ 0xF81F
colour_magentaData equ 0xD010
colour_blue equ 0x001F
colour_orange equ 0xE200
colour_dark_yellow equ 0xFFE0
colour_dark_green equ 0x07C0
colour_PacMan equ 0xE200
colour_blockNumber equ 0xE200
[BITS 16] ; Real Mode code (16 bit)
org RELOCATED
start:
codeStart:
jmp main_16bit ; 0x03 bytes | EB 58 90 00 Jump to boot code
times 3 - ($ - $$) nop ; fill with 1 or 0 no-ops to address 3
; BIOS boot parameter table = 0x25 bytes
db 'cf2019 0' ; 03 Eight byte OEM name
dw 0x0200 ; 11 Number of Bytes Per Sector
db 0x08 ; 13 Number of Sectors Per Cluster
dw 0x05E0 ; 14 Number of Reserved Sectors until the FAT
db 0x02 ; 16 Number of Copies of FAT : always = 2
dw 0x0000 ; 17 Maximum number of Root Directory Entries
dw 0x0000 ; 19 Not used for FAT32
db 0xF8 ; 21 Media type F0 = 1.44M 3.5 inch floppy disk, F8 = hard disk
dw 0x0000 ; 22 Sectors Per FAT for FAT12 and FAT16 - not used for FAT32
dw 0x003F ; 24 Sectors per Track
dw 0x00FF ; 26 Number of heads
dd 0x00000038 ; 28 Hidden sectors preceding the partition that contains this FAT volume
dd 0x007477C8 ; 32
dd 0x00001D10 ; 36 Sectors Per FAT for FAT32
dw 0x0000 ; 40
dw 0x0000 ; 42
dd 0x00000002 ; 44 Start of all directories, including root.
dw 0x0001 ; 48
dw 0x0006 ; 50 Offset in sectors from this sector to the backup BPB sector
; times 12 db 0 ; 0x0C bytes | 00 00 00 00 00 00 00 00 00 00 00 00 52
; db 0x00 ; 64
; db 0x00 ; 65
; db 0x29 ; 66 Extended Boot Signature
; dd 0x44444444 ; 67 serial number
; db 'colorForth ' ; 71 Eleven byte Volume Label
; db 'cFblocks' ; 82 Eight byte File System name
; ******************************************************************************
; ******************************************************************************
align 8, nop ; has to be aligned to 8 for GDT
; Note : we are NOT using null descriptor as GDT descriptor, see: http://wiki.osdev.org/GDT_Tutorial
; "The null descriptor which is never referenced by the processor. Certain emulators, like Bochs, will complain about limit exceptions if you do not have one present.
; Some use this descriptor to store a pointer to the GDT itself (to use with the LGDT instruction).
; The null descriptor is 8 bytes wide and the pointer is 6 bytes wide so it might just be the perfect place for this."
gdt: ; the GDT descriptor
dw gdt_end - gdt - 1 ; GDT limit
dw gdt0 + BOOTOFFSET ; pointer to start of table, low 16 bits
dw 0 , 0 ; the high bits of the longword pointer to gdt
gdt0: ; null descriptor
dw 0 ; 0,1 limit 15:0
dw 0 ; 2,3 base 15:0
db 0 ; 4 base 23:16
db 0 ; 5 type
db 0 ; 6 limit 19:16, flags
db 0 ; 7 base 31:24
code32p_SELECTOR_0x08 equ $ - gdt0
; bytes 1 0 3 2 5 4 7 6
dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit protected-mode code, limit 0xFFFFF
data32p_SELECTOR_0x10 equ $ - gdt0
dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit protected-mode data, limit 0xFFFFF
code16r_SELECTOR_0x18 equ $ - gdt0
dw 0xFFFF, 0x0000, 0x9A00, 0x0000 ; 16-bit real-mode code, limit 0xFFFFF
data16r_SELECTOR_0x20 equ $ - gdt0
dw 0xFFFF, 0x0000, 0x9200, 0x0000 ; 16-bit real-mode data, limit 0xFFFFF
gdt_end:
; ******************************************************************************
; ******************************************************************************
; align to 4 so we can access variables from high-level Forth
align 4, nop
data_area: ; data area begins here
bootsector: ; LBA of boot sector
dd 0
; save disk information, cylinder, sector, head and drive from BIOS call
driveinfo_Drive_DX: ; use low byte to store boot Drive into from BIOS DL
dw 0
driveinfo_CX: ; [7:6] [15:8][7] logical last index of cylinders = number_of - 1 (because index starts with 0)
; [5:0][7] logical last index of sectors per track = number_of (because index starts with 1)
dw 0
; cylinders, sectors, heads of boot drive
; low word: high byte is head
; high word: cylinder and sector: C76543210 C98S543210
driveinfo_Cylinder:
db 0
driveinfo_Head:
db 0
driveinfo_SectorsPertrack:
dw 0
align 4, nop
destination:
dd RELOCATED
dispPtr:
dd 0x00000140
v_bytesPerLine:
dd 0x00
v_scanCode:
dd 0x00
align 4
; ******************************************************************************
; the main program called from initial 16 bit mode
; ******************************************************************************
main_16bit:
cli ; clear interrupts
; turns out we don't need interrupts at all, even when using BIOS routines
; but we need to turn them off after disk calls because BIOS leaves them on
push si ; need to transfer SI to unused register BX later
; note: cannot touch DX or BP registers until we've checked for partition boot
; (SI could be used as well as BP but we use SI for relocation)
;see mbrboot.nasm
; Note : relocate the bootblock before we do anything else
pop bx ; we cannot use the current stack after changing SS or SP
; ... because mbrboot.nasm places stack at 0x7c00, in SECTOR_BUFFER
; and we cannot use BP because its default segment is SS
xor ax, ax
mov ds, ax
mov es, ax
mov si, BOOTOFFSET
mov di, SECTOR_BUFFER
mov sp, di
mov cx, 0x100
rep movsw ; note that this instruction doesn't change AX , it moves DS:SI to ES:DI and increments SI and DI
mov ss, ax ; stack segment also zero
mov ah, 0xb8 ; video RAM
mov gs, ax ; store in unused segment register
lgdt [gdt - $$ + BOOTOFFSET]
call SetupUnrealMode ; gs and ss must be initialized before going to Unreal Mode
; *****************************************************************************
; Enable the A20 address line, otherwise all odd 1 MByte pages are disabled
; Using the "PS/2 Controller" or 8042 "Keyboard controller"
; *****************************************************************************
; from http://wiki.osdev.org/%228042%22_PS/2_Controller#Step_1:_Initialise_USB_Controllers
; Write a command to the on-board 8042 "Keyboard controller" port 0x64 :
; 0x20 Read "byte 0" from internal RAM Controller Configuration Byte
; 0x21 to 0x3F Read "byte N" from internal RAM (where 'N' is the command byte & 0x1F)
; 0x60 Write next byte to "byte 0" of internal RAM (Controller Configuration Byte)
; 0x61 to 0x7F Write next byte to "byte N" of internal RAM (where 'N' is the command byte & 0x1F)
; 0xA7 Disable second PS/2 port
; 0xA8 Enable second PS/2 port
; 0xA9 Test second PS/2 port
; 0x00 test passed
; 0x01 clock line stuck low
; 0x02 clock line stuck high
; 0x03 data line stuck low
; 0x04 data line stuck high
; 0xAA Test PS/2 Controller
; 0x55 test passed
; 0xFC test failed
; 0xAB Test first PS/2 port
; 0x00 test passed
; 0x01 clock line stuck low
; 0x02 clock line stuck high
; 0x03 data line stuck low
; 0x04 data line stuck high
; 0xAC Diagnostic dump (real all bytes of internal RAM) Unknown
; 0xAD Disable first PS/2 port None
; 0xAE Enable first PS/2 port None
; 0xC0 Read controller input port Unknown (none of these bits have a standard/defined purpose)
; 0xC1 Copy bits 0 to 3 of input port to status bits 4 to 7 None
; 0xC2 Copy bits 4 to 7 of input port to status bits 4 to 7 None
; 0xD0 Read Controller Output Port Controller Output Port (see below)
; 0xD1 Write next byte to Keyboard Controller Output Port Note: Check if output buffer is empty first
; 0xD2 Write next byte to first PS/2 port output buffer
; 0xD3 Write next byte to second PS/2 port output buffer
; 0xD4 Write next byte to second PS/2 port input buffer
; 0xF0 to 0xFF Pulse output line low for 6 ms.
; Bits 0 to 3 are used as a mask (0 = pulse line, 1 = do not pulse line) and correspond to 4 different output lines.
; Bit 0 is the "reset" line, active low.
mov al, 0xD1 ; 0xD1 = Write next byte to Keyboard Controller Output Port
out 0x64, al ; On-board controller Command Write
.back:
in al, 0x64
and al, 0x02
jnz .back
mov al, 0x4B
out 0x60, al
; *****************************************************************************
; Get disk drive parameters from the BIOS
; *****************************************************************************
mov di, (data_area - $$ + BOOTOFFSET) ; setup the data index pointer
xor eax, eax
bts eax, 16 ; in case NOT booted from partition: sector 1, head 0, cylinder 0
or dh, dh ; booted from partition?
jz .forward3
mov eax, [ bx + 8 ] ; SI (now BX) contains pointer to partition record
mov [ byte di + (bootsector - data_area) ], eax ; offset 8 was LBA of first absolute sector
mov eax, [bx] ; CHS of first sector in partition
.forward3:
mov al, dl ; bootdrive into AL
mov [ word di + ( driveinfo_Drive_DX - data_area) ], eax ; save the Drive info from BIOS
mov ah, 8 ; get drive parameters
push es ; this operation messes with ES
push di ; and DI
mov di, DISK_INFO ; point di at the table returned by this software interrupt
int 0x13
jc $ ; stop here on error
call ReSetupUnrealMode
pop di
pop es
; ******************************************************************************
; load the bootdisk into both low and high RAM
; ******************************************************************************
mov [ byte di + ( driveinfo_Cylinder - data_area) ], dx ; heads in high byte
and cl, 0x3F ; we don't care about two high bits of cylinder count
mov [ byte di + ( driveinfo_SectorsPertrack - data_area) ], cx ; cylinders and sectors/track
mov dx, [ byte di + ( driveinfo_Drive_DX - data_area) ] ; restore dl Drive value from BIOS, dh = 0
; mov dl, 0x80
mov cx, [ di + ( driveinfo_CX - data_area) ] ; restore cl value, ch = 0
mov si, SECTORS_TO_LOAD
mov bx, SECTOR_BUFFER ; relocate the sector we are running from
call relocate
mov bx, BOOTOFFSET ; we will fix this below by adding 0x200
; remember the sector is 1-based, head and cylinder both 0-based
.nextsector:
inc cl
dec si
jz setVideoMode ; success, so setup the video now...
.bootload:
mov ax, 0x201 ; read 1 sector
add bh, 0x02 ; into next available slot in RAM
jnz .forward
sub bh, 0x02 ; at 0x10000 we go back to 0xfe00
.forward:
int 0x13
call ReSetupUnrealMode
jc $ ; stop here on error
call relocate
mov al, cl
and al, 0x3F ; low 6 bits
cmp al, [ byte di + ( driveinfo_SectorsPertrack - data_area) ]
jnz .nextsector
inc dh ; next head
cmp dh, [ byte di + ( driveinfo_Head - data_area) ]
jna .forward2 ; not JNZ, the head index is 1 less than head count
xor dh, dh
inc ch ; next cylinder
jnz .forward2
add cl, 0x40 ; bit 8 of cylinder count
.forward2:
and cl, 0xC0 ; clear sector count, low 6 bits of cl
jmp short .nextsector
; ******************************************************************************
; ******************************************************************************
; Start here after loading the program
; ******************************************************************************
; ******************************************************************************
; From : VESA BIOS EXTENSION (VBE) Core Functions Standard Version: 3.0 Date: September 16, 1998
; Mandatory information for all VBE revisions
; dw ModeAttributes ; 0x00 mode attributes
; db WinAAttributes ; 0x02 window A attributes
; db WinBAttributes ; 0x03 window B attributes
; dw WinGranularity ; 0x04 window granularity
; dw WinSize ; 0x06 window size
; dw WinASegment ; 0x08 window A start segment
; dw WinBSegment ; 0x0A window B start segment
; dd WinFuncPtr ; 0x0C real mode pointer to window function
; dw BytesPerScanLine ; 0x10 bytes per scan line <--------------
; Mandatory information for VBE 1.2 and above
; dw XResolution ; 0x12 horizontal resolution in pixels <-------------- scrnw
; dw YResolution ; 0x14 vertical resolution in pixels <-------------- scrnh
; db XCharSize ; 0x16 character cell width in pixels
; db YCharSize ; 0x17 character cell height in pixels
; db NumberOfPlanes ; 0x18 number of memory planes
; db BitsPerPixel ; 0x19 bits per pixel <-------------- bpp
; db NumberOfBanks ; 0x1A number of banks
; db MemoryModel ; 0x1B memory model type
; db BankSize ; 0x1C bank size in KB
; db NumberOfImagePages ; 0x1D number of images
; db Reserved ; 0x1E reserved for page function <-------------- mode (we copy it here)
; Direct Color fields (required for direct/6 and YUV/7 memory models)
; db RedMaskSize ; 0x1F size of direct color red mask in bits
; db RedFieldPosition ; 0x20 bit position of lsb of red mask
; db GreenMaskSize ; 0x21 size of direct color green mask in bits
; db GreenFieldPosition ; 0x22 bit position of lsb of green mask
; db BlueMaskSize ; 0x23 size of direct color blue mask in bits
; db BlueFieldPosition ; 0x24 bit position of lsb of blue mask
; db RsvdMaskSize ; 0x25 size of direct color reserved mask in bits
; db RsvdFieldPosition ; 0x26 bit position of lsb of reserved mask
; db DirectColorModeInfo ; 0x27 direct color mode attributes
; Mandatory information for VBE 2.0 and above
; dd PhysBasePtr ; 0x28 physical address for flat memory frame buffer <-------------- vframe
; dd Reserved ; 0x2C Reserved - always set to 0
; dw Reserved ; 0x30 Reserved - always set to 0
; Mandatory information for VBE 3.0 and above
; dw LinBytesPerScanLine ; 0x32 bytes per scan line for linear modes
; db BnkNumberOfImagePages ; 0x34 number of images for banked modes
; db LinNumberOfImagePages ; 0x35 number of images for linear modes
; db LinRedMaskSize ; 0x36 size of direct color red mask (linear modes)
; db LinRedFieldPosition ; 0x37 bit position of lsb of red mask (linear modes)
; db LinGreenMaskSize ; 0x38 size of direct color green mask (linear modes)
; db LinGreenFieldPosition ; 0x39 bit position of lsb of green mask (linear modes)
; db LinBlueMaskSize ; 0x3A size of direct color blue mask (linear modes)
; db LinBlueFieldPosition ; 0x3B bit position of lsb of blue mask (linear modes)
; db LinRsvdMaskSize ; 0x3C size of direct color reserved mask (linear modes)
; db LinRsvdFieldPosition ; 0x3D bit position of lsb of reserved mask (linear modes)
; dd MaxPixelClock ; 0x3E maximum pixel clock (in Hz) for graphics mode
; times 189 db 0 ; 0x42 remainder of ModeInfoBlock
; End ; 0xFF
scanVESA: ; ( w+h+b -- ) in ax
mov bx, ax
push di ; save di
mov cx, ( 0x4117 - 1 ) ; start scanning from the expected VESA mode 0x4117 ( the -1 is because of the inc cx below )
.back:
inc cl ; increment just the bottom byte, we test 0x41xx
cmp cl, 0x16 ; scanned from 0x4117 to 0x4116, not found, so show error
jz .failure
mov di, VESA_BUFFER ; buffer for the VESA mode information block
mov ax, 0x4F01 ; INT 0x10, AX=0x4F01, CX=mode Get Mode Info
int 0x10
cmp al, 0x4F ; success code = 0x4F
jne .back ; try the next VESA mode
mov ax, [di + 0x12] ; width
add ax, [di + 0x14] ; height
add al, [di + 0x19] ; bits per pixel
; adc ah, 0 ; should not be necessary for the expected result, 0x400+0x300+0x10
cmp ax, bx ; width + height + bits per pixel
je .success
jne .back ; try the next VESA mode
.failure: ; VESA mode not found, so continue
pop di ; restore di
mov ax, 0 ; return flag false
add ax, 0 ; set the zero flag
ret
.success:
mov [ di + ( vesa_SavedMode - VESA_BUFFER ) ], cx ; save the VESA mode in the VESA_BUFFER at offset 0x1E "Reserved"
mov ax, 1 ; return flag true
add ax, 0 ; set the zero flag
pop di ; restore di
ret
setVESA: ; we found a valid VESA mode
push ds ; clear all flags including Interrupt using DS, known to be zero
popf ; this is necessary to clear T flag also, end register display
call greet ; show greeting message
mov bx, cx
mov ax, 0x4F02 ; INT 0x10, AX=0x4F02, BX=mode, ES:DI=CRTCInfoBlock Set Video Mode
int 0x10
jmp main_32bit
setVideoMode:
%if ( FORCE_800x600_VESA == 0 ) ; test the 800x600 mode in bochs, which supports 1024x768
mov ax, ( 1024 + 768 + BITS_PER_PIXEL ) ; try the highest resolution first
call scanVESA ; if VESA mode is found, jump to setVESA
jnz setVESA ; success - we found the requested VESA mode
%endif
mov ax, ( 800 + 600 + BITS_PER_PIXEL ) ; then try a lower resolution
call scanVESA ; if VESA mode is found, jump to setVESA
jnz setVESA ; success - we found the requested VESA mode
; mov ax, 640 + 480 + BITS_PER_PIXEL ; then try an even lower resolution
; call scanVESA ; if VESA mode is found, jump to setVESA
; jnz setVESA ; success - we found the requested VESA mode
jmp showVESAerror ; we have tried all VESA modes without success, so report an error
; ******************************************************************************
; ******************************************************************************
relocate: ; copy 512 bytes from [bx] to FS:[destination]
pusha
mov cx, 0x200 / 2
mov si, bx
mov ebx, [ byte di + ( destination - data_area) ]
.back:
lodsw ; load the 16 bit value pointed to by SI into ax
mov [fs:ebx], ax ; Note : the fs: uses the 32 bit FS value setup in Unreal Mode to move the data outside of the 1 Mbyte Real Mode address range
add ebx, byte +2
loop .back
mov [ byte di + ( destination - data_area) ], ebx
popa
ret
; not used because it is very slow :
; now set up for trap displaying registers on screen during bootup
; push cs
; push showstate - $$ + BOOTOFFSET
; pop dword [word +4]
; ******************************************************************************
; ******************************************************************************
;1. MasterBoot Record - MBR at Sector 0 (decimal 0) MBR
; Partition at offset 1BE
; BootSignature 0
; Start Head|Sector|Cylinder 1 1 0
; Partition Type B DOS 7.1+
; End Head|Sector|Cylinder FE 3F 3E5
; BPBsectorNumber 00 \ was 3F
; Size of partition (decimal) 16035777 sectors, 8210317824 bytes, 8017889 Ki bytes, 7830 Mi bytes, 8 Gi bytes
; Partition at offset 1CE
; BootSignature 0
; Start Head|Sector|Cylinder 0 0 0
; Partition Type 0 Empty partition
; End Head|Sector|Cylinder 0 0 0
; BPBsectorNumber 0
; Size of partition (decimal) 0 sectors, 0 bytes, 0 Ki bytes, 0 Mi bytes,
; pretend to be a Master Boot Record so that the BIOS will load us
times ( 0x000001BE - ( $ - $$ ) ) db 0x77
db 0x80, 0x01, 0x01, 0x00, 0x0B, 0xFE, 0xFF, 0xE5, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xAF, 0xF4, 0x00 ; 0x1BE DOS partition 0 working on PC
db 00, 00, 00, 00, 00, 00, 00, 00 ; 0x1CE first 8 bytes of empty partition 1
SetupUnrealMode:
; set the FS segment in "unreal" mode, must be done before the Trap Flag is set in EFLAGS register
mov eax, cr0
or al, 1 ; set the "protected mode enable" bit => "unreal mode"
mov cr0, eax
push word data32p_SELECTOR_0x10 ; set the FS segment
pop fs
dec al ; clear the "protected mode enable" bit
mov cr0, eax
push ds ; now set FS to 0
pop fs
ReSetupUnrealMode:
push cs ; for iret
pushf ; for iret
pusha
mov bp, sp
mov ax, [bp + 16] ; get flags
; or ah, 0x01 ; set Trap Flag, bit 8 in the EFLAGS register ; debug only - very slow!
and ah, ~0x02 ; reset interrupt flag
xchg ax, [ bp + 20 ] ; swap flags with return address
mov [ bp + 16 ], ax ; return address at top of stack after popa
popa
iret
; ******************************************************************************
; ******************************************************************************
times 512 - 2 - ($ - $$) nop ; fill with no-ops to 55AA at end of boot sector
db 0x55 , 0xAA ; boot sector terminating bytes
; ******************************************************************************
; End of Boot Sector
; ******************************************************************************
; ******************************************************************************
; Show the user a null terminated string - writes directly into video RAM
; ******************************************************************************
displayString:
; restore the pointer to screen memory into di
mov di, (data_area - $$ + BOOTOFFSET)
mov ax, [ di + ( dispPtr - data_area) ]
mov di, ax
push es ; save es
mov ax, 0xb800 ; video RAM segment
mov es, ax
backhere2:
lodsb ; loads a byte from [ds:si] into al, then increments si
cmp al, 0
jz forward1 ; If al = 0 then leave the loop
mov ah, 0x0D ; text colour, magenta on black background
stosw ; stores ax into [es:di] then increments di
jmp backhere2
forward1:
; save the pointer to screen memory from di
mov ax, di
mov di, (data_area - $$ + BOOTOFFSET)
mov [ di + ( dispPtr - data_area) ], ax
pop es ; restore es
ret
; display a string then Wait for a key press
displayStringW:
pusha
call displayString
xor ax, ax ; wait for and get a key press ( AX = 0 )
int 0x16 ; BIOS interrupt Read a Key From the Keyboard
popa
ret
; msg_greeting2:
; db ' Press any key : ' , 0x00
msg_VESAerror:
db 'No valid VESA mode found! ' , 0x02, 0x00
; db ' No VESA mode ' , 0x02, 0x00
[BITS 16] ; Real Mode code (16 bit)
showVESAerror:
call greet
push si
mov word [ di + ( dispPtr - data_area) ] , 0x000001E0 ; line 3 0x50 x 2 x 3 = 0x1E0
mov si, ( msg_VESAerror - $$ + BOOTOFFSET ) ; string to display
call displayStringW
pop si
ret
greet: ; jump here to show 16 bit version text
push si
mov word [ di + ( dispPtr - data_area) ] , 0x00000140 ; line 2 0x50 x 2 x 2 = 0x140
mov si, ( version - $$ + BOOTOFFSET ) ; string to display
call displayString
; mov si, ( msg_greeting2 - $$ + BOOTOFFSET ) ; string to display
; call displayStringW
pop si
ret
; ******************************************************************************
; the main program in 32 bit ( protected ) mode
; ******************************************************************************
main_32bit:
call setProtectedModeAPI ; called from 16 bit code, returns in 32 bit code
[BITS 32] ; Protected Mode code (32 bit) - assemble for 32 bit mode from now on
mov esp, RETURN_STACK_0 ; setup the return stack pointer
mov esi, ( DATA_STACK_0 + 4 ) ; setup our data stack pointer
call save_BIOS_idt_and_pic ; to be restored later, when making BIOS calls
call init_default_PIC_IMRs ; set the default values and copy the BIOS Interrupt Vectors to our new table
_DUP_
mov _TOS_, INTERRUPT_VECTORS
call lidt_ ; Load the new Interrupt Descriptor Table
jmp dword warm
; *****************************************************************************
; calculate Cylinder, Head and Sector from zero-based sector number
; see http://teaching.idallen.com/dat2343/00f/calculating_cylinder.htm
; Note : uses pushad to copy registers onto the ESP stack, stores the
; calculated values onto the stack at the correct offsets, then restores the
; stack back to the registers.
; *****************************************************************************
sector_chs: ; ( sector -- eax ) calculate CHS from a sector number in eax,
; returns with DX = HHDD, CX = CCSS where HH=head, DD=drive, CC=cylinder, SS=sector
; Note that the input sector number is zero based, and that the high 16 bits of EAX must be 0
pushad ; Pushes all general purpose registers onto the stack in the following order:
; EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI. The value of ESP is the value before the actual push of ESP
; 7 6 5 4 3 2 1 0 offset in cells from ESP
mov ebp, esp ; copy the original ESP stack pointer to EBP so we can access items on the stack easily
; save the register values in the DAP buffer for use later, via ESI
mov esi, DAP_BUFFER
add eax, [ bootsector - $$ + BOOTOFFSET]
push eax ; save it while we calculate heads*sectors-per-track
mov al, [ driveinfo_Head - $$ + BOOTOFFSET] ; index of highest-numbered head
inc al ; 1-base the number to make count of heads
mul byte [ driveinfo_SectorsPertrack - $$ + BOOTOFFSET] ; sectors per track
mov ebx, eax
pop eax
xor edx, edx ; clear high 32 bits
div ebx ; leaves cylinder number in eax, remainder in edx
mov ecx, eax ; store cylinder number in another register
mov eax, edx ; get remainder into AX
mov bl, [ driveinfo_SectorsPertrack - $$ + BOOTOFFSET] ; number of sectors per track
div bl ; head number into AX, remainder into DX
mov bl, al ; result must be one byte, so store it in BL
rol ecx, 8 ; high 2 bits of cylinder number into high 2 bits of CL
shl cl, 6 ; makes room for sector number
or cl, ah ; merge cylinder number with sector number
inc cl ; one-base sector number
mov [ ebp + ( 6 * 4 ) ], ecx ; store the result in ECX position on esp stack
mov word [ esi + o_Int13_DAP_saved_CHS_CX ], cx ; also save the calculated CX value
mov cx, [ driveinfo_Drive_DX - $$ + BOOTOFFSET] ; drive number in low 8 bits
mov ch, bl ; place head number in high bits
; mov cl, 0x80
mov [ ebp + ( 5 * 4 ) ], ecx ; store the result in EDX position on esp stack
mov word [ esi + o_Int13_DAP_saved_CHS_DX ], cx ; also save the calculated DX value
popad ; restore registers from esp stack
ret
; *****************************************************************************
; enter Protected Mode (32 bit) and Real Mode (16 bit)
; from http://ringzero.free.fr/os/protected%20mode/Pm/PM1.ASM
; *****************************************************************************
[BITS 16] ; Real Mode code (16 bit)
enterProtectedMode: ; must come from a 'call' , can not be inlined
pop ax
push code32p_SELECTOR_0x08
push ax
retf
setProtectedModeAPI: ; set protected mode from 'Real' mode. Called from 16 bit code, returns to 32 bit code
pushad ; save all registers as doublewords
mov eax, cr0
or al, 1
mov cr0, eax ; set the Protected Mode bit in the Control Register
xor eax, eax ; clear high bits of eax
call enterProtectedMode
[BITS 32] ; Protected Mode code (32 bit)
mov eax, data32p_SELECTOR_0x10 ; Protected Mode data segment
mov es, ax
mov ds, ax
mov ss, ax ; this makes stack segment 32 bits
popad
o16 ret
enter16bitProtectedMode: ; 32 bit code. Must come from a 'call' , can not be inlined
pop eax ; return address
push dword code16r_SELECTOR_0x18 ; select 16-bit Protected Mode AKA 'Real' Mode
push eax
retf
setRealModeAPI: ; set 'Real' mode from protected mode.
; Called from 32 bit code, returns to 16 bit code
; assumed that protected-mode stack is based at 0
; and that bits 16 through 19 will not change during time in realmode
pushad ; save 32-bit values of registers
mov ecx, esp ; do all possible 32-bit ops before going to 16 bits
mov edx, cr0
call enter16bitProtectedMode
[BITS 16] ; Real Mode code (16 bit)
mov ax, data16r_SELECTOR_0x20
mov ds, ax
mov es, ax
mov ss, ax ; here the stack becomes 16 bits based at 0, and SP used not ESP
; *** consider stack to be invalid from here until we reach real mode ***
xor cx, cx ; clear low 16 bits
shr ecx, 4 ; move high 4 bits into cl
dec dl ; leave protected mode, only works if we KNOW bit 0 is set
mov cr0, edx
call enterRealMode
xor ax, ax
mov ds, ax
mov es, ax
mov ss, cx
; note we don't need to set SP to 8xxx if ESP is b8xxx, since
; the b000 is now in SS, and the b of b8xxx is ignored in real mode
popad
o32 ret
enterRealMode: ; 16 bit code. Must come from a 'call' , can not be inlined
pop ax
push fs ; real-mode code segment
push ax
retf
[BITS 32] ; Protected Mode code (32 bit)
; *****************************************************************************
; *****************************************************************************
;%include "JCreadwrite.nasm"
; JCreadwrite.nasm 2012 Oct 23 read and write the disk using 16 bit BIOS calls
; BIOS read and write routines for colorForth
[BITS 32] ; Protected Mode code (32 bit)
bios_read: ; ( a c -- a' c' ) \ read cylinder c into address a , leave next address and cylinder
; c is cylinder, we will use 1.44Mb floppy's idea of cylinder regardless
; a is byte address
; leave updated c and a on stack as c' and a'
; a cylinder is 36 tracks of 512 bytes each, 0x4800 bytes, 0x1200 cells (words)
cli ; disable interrupts
pushad ; push all registers ( except esp ) and flags onto the stack
mov ebp, esp ; copy of stack pointer for use below ( * ), points to registers copied by pushad , above
mov ecx, HEADS * SECTORS ; sectors per track (both heads)
mul cl ; sector number goes into AX
; note that resultant sector number is zero-based going into sector_chs!
; set up loop to read one floppy cylinder's worth
push eax ; absolute sector number to start
.back:
push ecx
call sector_chs ; convert to Cylinder-Head-Sector in CX-DX
call .readsector
mov ebx, [ ebp + ( 1 * 4 ) ] ; ( * ) get ESI stored on stack, via stack pointer saved in ebp
mov edi, [ebx] ; destination index address for movsd
mov ecx, ( 512 >> 2 ) ; number of 32-bit words to move, 512 bytes
mov esi, SECTOR_BUFFER ; source index for movsd
rep movsd ; copy ecx 32 bit words from ds:esi to es:edi
mov [ebx], edi
pop ecx
pop eax
inc eax
push eax
loop .back
pop eax
inc dword [ebp + 7 * 4] ; for updated cylinder number after return
popad
ret
.readsector: ; no need to save registers because we take care of them in calling routine
call setRealModeAPI
[BITS 16] ; Real Mode code (16 bit)
mov bx, SECTOR_BUFFER
mov ax, 0x0201 ; read 1 sector
int 0x13
cli ; BIOS might have left interrupts enabled
call setProtectedModeAPI ; called from 16 bit code, returns to 32 bit code
[BITS 32] ; Protected Mode code (32 bit)
ret
bios_write: ; ( a c -- a' c' ) \ write cylinder c from address a , leave next address and cylinder
cli ; disable interrupts
pushad
mov ebp, esp
; eax contains cylinder to start, the 'c' parameter
mov ecx, HEADS * SECTORS ; sectors per track (both heads)
mul cl ; absolute sector number goes into AX
mov ebx, [ebp + ( 1 * 4 ) ] ; stored ESI on stack
mov esi, [ebx] ; word address, 'a' parameter
; shl esi, 2 ; change word address into byte address
; set up loop to write one floppy cylinder's worth
push eax ; absolute sector number to start
.back:
push ecx
; load sector data into buffer
; DO NOT take advantage of knowing ECX only has byte value
mov ecx, 128 ; ( 512 >> 2 ) ; number of 32-bit words to move
mov edi, SECTOR_BUFFER
rep movsd ; copy ecx 32 bit words from ds:esi to es:edi
call sector_chs ; convert to Cylinder-Head-Sector in CX-DX
call .writesector
pop ecx
pop eax
inc eax
push eax
loop .back
pop eax
inc dword [ ebp + ( 7 * 4 ) ] ; for updated cylinder after return (EAX)
mov ebx, [ ebp + ( 1 * 4 ) ] ; stored ESI on stack
mov [ebx], esi ; updated address
popad
ret
.writesector: ; no need to save registers because we take care of them in calling routine
call setRealModeAPI
[BITS 16] ; Real Mode code (16 bit)
mov bx, SECTOR_BUFFER
mov ax, 0x0301 ; write 1 sector
int 0x13
cli ; BIOS might have left interrupts enabled
call setProtectedModeAPI ; called from 16 bit code, returns to 32 bit code
[BITS 32] ; Protected Mode code (32 bit)
ret
times (0x400 - ($ - $$)) nop
; *****************************************************************************
; *****************************************************************************
; After Two Sectors
; *****************************************************************************