This repository has been archived by the owner on Jun 8, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
int21.asm
813 lines (806 loc) · 18.4 KB
/
int21.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
use16
;;
;; This file contains the INT 21 API for the NOS kernel.
;; Table of functions follows:
;;
;; AX=0123h, BX=4567h, CX=8910h, DX=FFFFh: This invokes a kernel panic routine. This is not available
;; to user apps (the NOS kernel will call the panic with CF set and a byte in RAM set to FFh). This
;; sends a message to the user and halts the machine.
;;
;; AH=00h: Does nothing, returns AX=5555h
;; AH=01h: Write string to display (null terminated) from DS:DX, returns number of chars written
;; in BH.
;; AH=02h: Open a file. Opens file whose null-terminated name is in DS:DX, and loads the contents
;; of the file to ES:BX. Returns number of blocks read in AL, CF is set on an error, and
;; BH describes the error when CF is set.
;; AH=03h: Close a file. DS:DX points to the null-terminated file name, ES:BX points to the file
;; contents. Returns nothing on no error, if an error occurs then CF is set and BH describes
;; the error. If CF is set on entry, then will save as executable and will look for EOF marks
;; ("EF", 0xFF for flat; "ES", 0xFF for segmented)
;; AH=04h: Gets the NOS version and places two ASCII characters into CX. (e.g. CX="20", NOS 2.0)
;; Minor version number is placed in AL (e.g. Rev 3, AL="3")
;; AH=05h: No function is here yet, this will just IRET until something gets placed here.
;; AH=06h: Gets a string from the user until the user presses return. The string is placed into
;; RAM pointed to by DS:DX.
;; First we want to check if the kernel panic thing is being called.
pushf ; For later functions
cmp ax, 0123h
jne main_func_check
cmp bx, 4567h
jne main_func_check
cmp cx, 8910h
jne main_func_check
cmp dx, 0FFFFh
jne main_func_check
;; Now check for the RAM byte (0x9000:0xFFFF)
push es
push bx
mov bx, 9000h
mov es, bx
mov ah, byte ptr es:0FFFFh
pop bx
pop es
cmp ah, 55h
jne fraud_call
;; Check the carry flag
popf
jc kernel_panic
fraud_call:
;; At this point we can guarantee that this is a fraudulent panic call. Just do an IRET now
;; with the carry flag set.
or byte [esp+4], 1
iret
main_func_check:
;; Check for a non-panic call.
cmp ah, 00h
je install_check ; Our install check routine for boot time
cmp ah, 01h
je print_string ; Our print string function
cmp ah, 02h
je open_file ; Open file function
cmp ah, 03h
je close_file ; Close file function
cmp ah, 04h
je nos_version ; Returns tho NOS version
;; Skip 0x05 for now, created 0x06 with no 0x05! so I will have to think up a
;; function to fill this hole.
cmp ah, 06h
je get_user_string ; Our getstring function
;; If none of these match, pop our flags, set carry, and return
popf
; Set carry
or byte [esp+4], 1
iret
install_check:
;; This is a simple install check function.
;; Doesn't take any args, and just returns AX=0x5555
popf ; Required of all functions, to keep stack clean
mov ax, 5555h
iret
; This code snippet is provided by SeproMan on the FASM board.
; Thanks Sepro! :D
print_string:
popf
push ax
push cx
push si
mov ah, 0Eh ;BIOS teletype
mov bh, 0 ;Display page 0, don't care about BL in video mode 3
mov ch, 0 ;Count characters
mov si, dx ;DX can't be used to address memory!!!
print_loop:
mov al, [ds:si]
inc si
cmp al, 0
je print_done
int 10h
inc ch ;Printed a char
jmp print_loop
print_done:
mov bh, ch ;BH is be the number of chars that we wrote
pop si
pop cx
pop ax
iret
open_file:
popf
mov byte ptr exec_check, 00h
; Check to see if we are loading an executable file
mov ah, byte [esp+4]
and ah, 1
cmp ah, 1
; If so set the exec flag
jne start_open_file
mov byte ptr exec_check, 80h
; clear carry
clc
and byte [esp+4], -2
start_open_file:
push es
push bx
; Place the address to load up to in RAM
mov word ptr cs:loadto_off, bx
push es
pop bx
mov word ptr cs:loadto_seg, bx
pop bx
pop es
pusha
; Load up the FSB
mov ah, 02h
mov al, 01h
mov ch, 00h
mov dh, 00h
mov cl, 02h
; Get the boot drive from the kernel (4th byte in kernel space)
push ds
push ax
mov ax, 1000h
mov ds, ax
pop ax
mov dl, ptr ds:0003h
pop ds
mov bx, 2000h
mov es, bx
xor bx, bx
int 13h
jc disk_error
popa
push es
push bx
; Read through DS:DX to find the filename
mov si, dx
; DI is pointer to filename char
xor di, di
mov di, filename
open_filename_loop:
mov al, byte ptr ds:si
; Check for null
cmp al, 00h
je done_open_filename_loop
mov [cs:di], al
inc di
inc si
jmp open_filename_loop
done_open_filename_loop:
; Increment through the FSB starting at the file fields (8 bytes in)
; File field format:
; 0x80
; C,H,S of file (3 bytes)
; Number of blocks (1 byte)
; Filename with 0x00 padding (8 bytes)
; Executable flag (1 byte)
; 0xFF
mov bx, 2000h
mov es, bx
mov bx, 0007h
find_files_loop:
; Check for a file
mov ah, byte ptr es:bx
cmp ah, 80h
je found_field
; If there is a null here, then we are in empty spce of the FSB.
; Exit just in case.
cmp ah, 00h
je disk_error
inc bx
jmp find_files_loop
disk_error:
; Otherwise we are done here, and have not found our file.
pop bx
pop es
or byte [esp+4], 1
iret
found_field:
; inc through the field to the filename (pointing at 0x80)
inc bx
inc bx
inc bx
inc bx
inc bx
mov si, filename
; Compare the filenames
cmp_name_loop:
mov ah, byte ptr es:bx
mov al, byte ptr cs:si
cmp ah, 00h
je check_end_filename
cmp ah, al
; If not equal then go to the end of the field and check for another one
jne not_right_file
; inc and check next char
inc bx
inc si
jmp cmp_name_loop
check_end_filename:
; Looking at the end of the file name. If they are the same then we have the correct file and we need
; to load it up.
cmp ah, al
jne not_right_file
; Check if the file we are looking for is an executable file
cmp byte ptr exec_check, 80h
je check_for_exec
; We have a correct filename. Load it up, point at beginning of file field.
go_back_to_80:
dec bx
mov ah, byte ptr es:bx
cmp ah, 80h
jne go_back_to_80
; Get CHS info
inc bx
mov ch, byte ptr es:bx
inc bx
mov dh, byte ptr es:bx
inc bx
mov cl, byte ptr es:bx
; Now the number of blocks
inc bx
mov al, byte ptr es:bx
mov byte ptr cs:blocks, al
push ds
push ax
mov ax, 1000h
mov ds, ax
pop ax
mov dl, byte ptr ds:0003h
pop ds
pop es
pop bx
; Get the address
mov bx, word ptr cs:loadto_seg
mov es, bx
mov bx, word ptr cs:loadto_off
mov ah, 02h
int 13h
jc disk_error
; Move the blocks into AL
mov al, byte ptr cs:blocks
; Return filesystem string to expected values
mov cx, 08d
mov bx, filename
clear_data_loop_open:
mov si, cx
mov byte ptr cs:bx+si, 00h
loop clear_data_loop_open
; We are finished, quit
iret
not_right_file:
; Somewhere in the filename. Go back to the beginning of the field
dec bx
mov ah, byte ptr es:bx
cmp ah, 80h
jne not_right_file
; Back to 0x80. Go to byte after 0xff
add bx, 14d
jmp find_files_loop
check_for_exec:
; Pointing at the end of the file name
; Point to the exec flag (13 bytes in)
dec bx
mov ah, byte ptr es:bx
cmp ah, 80h
jne check_for_exec
add bx, 13d
cmp byte ptr es:bx, 80h
jne not_right_file
; We have a correct file name.
jmp go_back_to_80
open_file_data:
filename db 00h,00h,00h,00h,00h,00h,00h,00h ; 8 bytes for file name
blocks db 00h ; For the number of blocks later
loadto_seg dw 0000h ; For the segment to load the file to
loadto_off dw 0000h ; For the offset to load the file to
exec_check db 00h ; For the executable check flag
scratch db 00h ; For random stuff
close_file:
popf
; First thing first, update the FSB.
pusha
mov ah, 02h
mov al, 01h
mov ch, 00h
mov dh, 00h
mov cl, 02h
push ds
push ax
mov ax, 1000h
mov ds, ax
pop ax
mov dl, ptr ds:0003h
pop ds
mov bx, 2000h
mov es, bx
xor bx, bx
int 13h
jc disk_error_save
popa
; To begin saving a file, we need to see if we are saving an executable file or not.
; If so, we need to check for a specific sequence of characters rather than just the
; 0xFF EOF character to avoid truncating code.
; File is in ES:BX, name in DS:DX
jc save_as_exec
; Otherwise, we will save as a normal file.
; First we need to see if the file exists. We will use the empty space in the INT21
; segment for this purpose.
push es
push bx
push cs
pop es
mov bx, out_in_space
mov ah, 02h
int 21h
; If the file exists, then all we need to do is check the length of the file to see
; if we need to allocate more space in the FSB for it.
; If it doesn't, then we need to create a new FSB entry for it.
jc save_does_not_exist
; It does exist. Check the length of the file here.
mov cx, 0000h
push bx
check_length:
inc cx
mov al, byte ptr es:bx
cmp al, 0FFh
je done_check_length
; Not done, so inc to next byte and check again
inc bx
jmp check_length
done_check_length:
; Divide the number of bytes counted by 512 to get the total number of blocks
push dx
mov ax, cx
mov cx, 0200h ; 512 decimal
div cx
; Check to see if there is a remainder, and if so then add a block to the result
cmp dx, 0000h
je get_field_save
inc ax
; Save the count in CX
mov cx, ax
get_field_save:
; Now we need to find the file field for the file and check to see if we need to
; allocate more space.
pop dx
push es
push bx
mov bx, 2000h
mov es, bx
mov bx, 0008h
look_for_file_save:
mov ah, byte ptr es:bx
cmp ah, 80h
je found_field_save
pop dx
pop si
cmp ah, 00h
je disk_error_save
push si
push dx
inc bx
jmp look_for_file_save
found_field_save:
; Check to see if it is indeed our file.
; Pointing at 0x80
push bx
add bx, 05d
mov si, dx
push si
compare_filename_save:
mov ah, byte ptr es:bx
cmp ah, byte ptr ds:si
jne not_right_file_save
; Check for the end of the filename
cmp ah, 00h
je found_file_save
; Otherwise keep going
inc bx
inc si
jmp compare_filename_save
not_right_file_save:
pop si
; Go back to the 0x80
pop bx
; Now to the next field
add bx, 14d
jmp look_for_file_save
found_file_save:
; We have our file. Go back to the 0x80 and look at the length in blocks.
pop si
pop bx
add bx, 04d ; Pointing at length
mov ah, cl ; FSB limitation: can't save more than 255 blocks at once
mov al, byte ptr es:bx
cmp ah, al
jg allocate_more_space_save
; If we have something that is less than the curently allocated space, we will leave it there just to
; keep things simple
jmp save_file
allocate_more_space_save:
; Currently not implemented
pop ax
pop ax
mov ah, 01h
push ds
push cs
pop ds
mov dx, allocate_ns
int 21h
pop ds
iret
allocate_ns db "WARN: Extra space allocation is currently not implemented in INT21!", 0Dh, 0Ah, 00h
save_file:
; We have length in cl, move it to al
mov al, cl
mov ah, 03h
; Get the CHS information
sub bx, 03h
mov ch, byte ptr es:bx
inc bx
mov dh, byte ptr es:bx
inc bx
mov cl, byte ptr es:bx
pop bx
pop es
; Get boot drive from kernel
push ds
push si
mov si, 1000h
mov ds, si
mov dl, byte ptr 0003h
pop si
pop ds
int 13h
jc disk_error_save
; We are successful! Return here.
iret
save_as_exec:
; Currently not implemented
mov ah, 01h
push cs
pop ds
mov dx, save_as_exec_ns
int 21h
iret
save_as_exec_ns db "WARN: Saving as executable currently not implemented!", 0Dh, 0Ah, 00h
disk_error_save:
or byte [esp+4], 1
iret
save_does_not_exist:
; First, we need to find the last used sector on the disk.
; We will do this by incrementing through the FSB fields until we
; find something that is the last physical file on the disk. We will
; then save the file there.
push ds
push es
push bx
push cs
pop ds
mov bx, 2000h
mov es, bx
mov bx, 0008h
last_file_loop:
; Find a 0x80
mov ah, byte ptr es:bx
; If we have a null, we are out in spcae after the file fields
cmp ah, 00h
je done_last_file
; If we have a 0x80, then we need to go on out to the CHS and figure out
; if it is further out on the disk than the last recorded value
cmp ah, 80h
je found_field_last
; Otherwise increment and continue looking
inc bx
jmp last_file_loop
found_field_last:
; First thing to look at is the cylinder (track) value.
; Pointing at 0x80
inc bx
mov si, 0000h
mov ah, byte ptr es:bx
mov al, byte ptr last_file_chs
cmp ah, al
; If they are equal then move on to the head value, otherwise we should
; check to see if it is greater. If so, then this file is further out than
; our current recorded value.
je check_field_head
; If less than then this file is further inward, so ignore
jl not_outward_save
; It is greater than. Value for C is in AH.
mov byte ptr last_file_chs, ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
xor si, si
; Now we need to go to the end of the field and keep looking.
add bx, 10d
jmp last_file_loop
check_field_head:
; Pointing at the cylinder value
inc bx
inc si
mov ah, byte ptr es:bx
mov al, byte [last_file_chs+si]
cmp ah, al
je check_field_sector
jl not_outward_save
; Otherwise, decrement and move the values in
dec bx
dec si
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
add bx, 10d
jmp last_file_loop
check_field_sector:
inc bx
inc si
mov ah, byte ptr es:bx
mov al, byte [last_file_chs+si]
cmp ah, al
jl not_outward_save
; IF they are equal then there is an error somewhere in the form of a duplicate
; file field.
je disk_error_save
sub bx, 02d
sub si, 02d
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
mov ah, byte ptr es:bx
mov byte [last_file_chs+si], ah
inc si
inc bx
add bx, 10d
jmp last_file_loop
not_outward_save:
; If we are here then this field is closer to the beginning of the disk.
; We need to go to the end of the field.
dec bx
mov ah, byte ptr es:bx
cmp ah, 80h
jne not_outward_save
add bx, 14d
; Now back to the top of the loop
jmp last_file_loop
done_last_file:
; What we have now is the last file on disk's CHS stored in RAM.
; We need to save the file one sector after this one.
;; NOTE: FOR NOW WE WILL ASSUME THE BOOT DISK IS A FLOPPY. I AM AWARE THAT THIS IS NOT A GOOD PRACTICE.
;; PROPER HARD DISK SUPPORT WILL BE IMPLEMENTED AT A LATER DATE.
pop bx
pop es
; To increment the value, we need to check to make sure that the CHS values stay within their limits.
mov si, 0003d
cmp byte [last_file_chs+si], 18d
; If so then we need to carry. Otherwise, we can just increment the sector value and be fine.
je carry_chs_save
inc byte [last_file_chs+si]
; Now we need to start working on creating the file field.
jmp create_new_field
carry_chs_save:
; We need to carry the sector value over, so reset it to zero and add one to the head value. If needed, we
; also can carry that, and if we overflow we need to display a "disk full" message to the user.
mov byte [last_file_chs+si], 00h
dec si
cmp byte [last_file_chs+si], 01d
je carry_over_head
; Otherwise, increment the head and move right along
inc byte [last_file_chs+si]
jmp create_new_field
carry_over_head:
mov byte [last_file_chs+si], 00h
dec si
cmp byte [last_file_chs+si], 79d
je disk_full
inc byte [last_file_chs+si]
jmp create_new_field
disk_full:
mov ah, 01h
mov dx, disk_full_msg
int 21h
pop ds
jmp disk_error_save
disk_full_msg db "ERR: Attempted to save to a disk that has no space for the file.", 0Dh, 0Ah, 00h
create_new_field:
; We have the needed CHS info to create a new field. All we need to do now is get the length of the file
; by looping through it until we hit an EOF.
call get_file_length_save
; Run through all of the file fields until we reach null space
push es
push bx
mov bx, 2000h
mov es, bx
mov bx, 0008h
find_null_space:
mov ah, byte ptr es:bx
cmp ah, 80h
je skip_field
cmp ah, 00h
je in_null_space
inc bx
jmp find_null_space
skip_field:
add bx, 14d
jmp find_null_space
in_null_space:
; We should be just pointing at the first null.
mov byte ptr es:bx, 80h
; Add the CHS values
inc bx
xor si, si
mov ah, byte [last_file_chs+si]
mov byte ptr es:bx, ah
inc bx
inc si
mov ah, byte [last_file_chs+si]
mov byte ptr es:bx, ah
inc bx
inc si
mov ah, byte [last_file_chs+si]
mov byte ptr es:bx, ah
inc bx
mov si, bx
push es
pop gs
; Now the length
mov ah, byte ptr length_save
mov byte ptr es:bx, ah
pop bx
pop es
pop ds
push es
push bx
push gs
pop es
mov bx, si
; Now put the filename in there
mov cl, 08d
xor ch, ch
mov si, dx
add_filename:
mov ah, byte ptr ds:si
dec cl
; Check to see if the char is a null
cmp ah, 00h
je pad_filename
; Otherwise we need to put the char and continue
mov byte ptr es:bx, ah
inc bx
inc si
jmp add_filename
pad_filename:
inc bx
mov byte ptr es:bx, 20h
loop pad_filename
; Now we need to write the EXE flag and end byte
inc bx
mov byte ptr es:bx, 00h
mov byte ptr es:bx, 0FFh
; Now write the FSB to disk
mov ah, 03h
mov al, 01h
mov ch, 00h
mov dh, 00h
mov cl, 02h
push ds
mov bx, 1000h
mov ds, bx
mov dl, byte ptr ds:0003h
pop ds
mov bx, 2000h
mov es, bx
mov bx, 0000h
int 13h
jc disk_error_save
; Now write the actual file to disk
mov ah, 03h
push cs
pop ds
mov al, byte ptr length_save
mov si, 0000h
mov ch, byte [last_file_chs+si]
inc si
mov dh, byte [last_file_chs+si]
inc si
mov cl, byte [last_file_chs+si]
push ds
mov bx, 1000h
mov ds, bx
mov dl, byte ptr ds:0003h
pop ds
pop bx
pop es
int 13h
jc disk_error_save
; We are done.
iret
get_file_length_save:
pusha
mov cx, 0000h
get_file_length_loop:
mov ah, byte ptr es:bx
inc cx
cmp ah, 0FFh
jne get_file_length_loop
; Got our length in bytes, now divide it down
xor dx, dx
mov ax, cx
mov cx, 0200h
div cx
cmp dx, 0000h
je got_file_length_save
inc ax
got_file_length_save:
mov byte ptr length_save, al
popa
ret
last_file_chs db 00h,00h,00h
length_save db 00h
get_user_string:
popf
; Args:
; DS:DX - address to save string at
mov si, dx
get_char_loop:
; Start by getting a char
mov ah, 00h
int 16h
; Check to see if AL is a CR
cmp al, 0Dh
je get_string_done
; Otherwise copy the char and begin again
mov [ds:si], al
inc si
mov ah, 0Eh
int 10h
jmp get_char_loop
get_string_done:
; Place our NULL, print a return, and exit
mov byte [ds:si], 00h
xor si, si
push ax
push bx
mov ah, 0Eh
mov al, 0Dh
mov bh, 00h
int 10h
mov al, 0Ah
int 10h
pop bx
pop ax
iret
nos_version:
popf
mov cx, "B4"
mov al, "0"
iret
kernel_panic:
; Turn on all keylights and Halt
mov dx, 60h
mov al, 0EDh
out dx, al
mov ax, 00000111b
out dx, al
cli
hlt
jmp kernel_panic
out_in_space:
times 1536-($-$$) db 00h