Advertisement
Guest User

Untitled

a guest
Jan 18th, 2018
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. .386p ; Чтобы разрешить транслятору обрабатывать расширенный набор команд 32-разрядного микропроцессора, часть к-х отн. к привелигерованным
  2.  
  3. ; ОПИСАНИЕ СТРУКТУР
  4.  
  5. ;структура для описания декскриптора сегмента
  6. descr struc
  7.     lim     dw 0  ; Граница (биты 0..15), номер последнего байта сегмента
  8.     base_l  dw 0  ; База, биты 0..15, начальный линейный адресс(номер байта, а не сегмент:смещение) сегмента в адресном пространстве процессора
  9.     base_m  db 0  ; база, биты 16..23
  10.     attr_1  db 0  ; Байт атрибутов 1, характеристики сегментов.сегмент команд - 98h, сегмент данных(или стека) - 92h
  11.     attr_2  db 0  ; Граница(биты 16..19) и атрибуы 2, страничная адресация или нет, тип дробности
  12.     base_h  db 0  ; База, биты 24..31
  13. descr ends
  14.  
  15. ;структура для описания шлюзов ловушек
  16. trap struc
  17.     offs_l  dw 0  ; Смещение обработчика, биты 0..15
  18.     sel     dw 0 ; Селектор сегмента команд
  19.     cntr    db 0  ; Счетчик - не используется
  20.     dtype   db 8Fh; Тип шлюза - ловушка 80386 и выше
  21.     offs_h  dw 0  ; Смещение обработчика, биты 16..31
  22. trap ends
  23.  
  24. ; СЕГМЕНТ КОДА ЗАЩИЩЕННОГО РЕЖИМА
  25. PROTECTED_MODE_SG SEGMENT PARA PUBLIC 'CODE' USE32
  26.     ASSUME CS:PROTECTED_MODE_SG
  27.     ; GDT - таблица глобальных дескрипторов GDT, в защищенном режиме обращение к сегментам возможно только через их дескрипторы
  28.     gdt label byte 
  29.         gdt_null     descr <0, 0, 0, 0, 0> ; Селектор 0, нулевой дескриптор
  30.         gdt_flat     descr <0FFFFh, 0, 0, 92h, 11001111b, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
  31.         gdt_code_16  descr <REAL_MODE_SG_size - 1, 0, 0, 98h, 0, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
  32.         gdt_code_32  descr <PROTECTED_MODE_SG_size - 1, 0, 0, 98h, 01000000b, 0>; Селектор 16, сегмент команд, 98h - исполняемый сегмент, запрещено чтение и запись
  33.         gdt_data     descr <PROTECTED_MODE_SG_size - 1, 0, 0, 92h, 11001111b, 0> ; Селектор 8, сегмент данных, 92h - разрешены чтение и запись
  34.         gdt_stack    descr <STACK_SIZE - 1, 0, 0, 92h, 01000000b, 0>; Селектор 24, сегмент стека
  35.     gdt_size = $-gdt ; Размер GDT для передачи процессору вместе в адресом GDT
  36.     gdtr        df 0
  37.  
  38.     ; имена для селекторов
  39.     SELECTOR_FLAT       equ 8
  40.     SELECTOR_CODE_16    equ 16
  41.     SELECTOR_CODE_32    equ 24
  42.     SELECTOR_DATA       equ 32
  43.     SELECTOR_STACK      equ 40
  44.  
  45.     ; IDT
  46.     idt label byte                                  ; Метка начала таблицы дескрипторов исключений - IDT
  47.               trap 32 dup (<dummy, SELECTOR_CODE_32, 0, 8Eh>) ; Первые 32 исключения
  48.         int08 trap <0, SELECTOR_CODE_32, 0, 8Eh>    ; Обработчик прерываний от таймера
  49.         int09 trap <0, SELECTOR_CODE_32, 0, 8Eh>    ; Обработчик прерываний от клавиатуры
  50.     idt_size = $-idt                                ; Размер IDT
  51.     idtr        df 0
  52.     idtr_real   dw  3FFh, 0, 0                      ; содержимое регистра idtR в реальном режиме
  53.     master      db 0                                ;маска прерываний ведущего контроллера
  54.     slave       db 0                                ;маска прерываний ведомого контроллера
  55.     escape      db 0                                ;флаг - пора выходить в реальный режим, если ==1
  56.     time_08     dd 0                                ;счетчик прошедших тиков таймера
  57.    
  58.     ; Таблица перевода
  59.     scan2ascii  db  0,1Bh,'1234567890-=',8,0
  60.                 db  'qwertyuiop[]',13,0
  61.                 db  'asdfghjkl;''',0
  62.                 db  0,0,'zxcvbnm,./',0,0,0,' '
  63.        
  64.     ; Позиция начала ввода
  65.     input_pos dd 0320h
  66.    
  67.     ; Сообщение - реальный режим
  68.     msg         db  'Return to real mode!', 27, '[0m$'
  69.    
  70.     ; Сообщение - защищенный режим
  71.     msg_prot    db  'P',4,'r',4,'o',4,'t',4,'e',4,'c',4,'t',4,'e',4,'d',4
  72.                 db  ' ',4
  73.                 db  'm',4,'o',4,'d',4,'e',4,' ',4, 'o', 4, 'n', 4
  74.     msg_prot_size = $-msg_prot
  75.        
  76.     ; Сообщение - таймер
  77.     msg_time    db  'T',6,'i',6,'m',6,'e',6,'r',6,':',6
  78.     msg_time_size = $-msg_time
  79.  
  80.     ; Сообщение - память
  81.     msg_mem db  'M',5,'e',5,'m',5,'o',5,'r',5,'y',5,':',5
  82.     msg_mem_size = $-msg_mem
  83.    
  84. ; точка входа в 32-битный защищенный режим
  85. PROTECTED_MODE_ENTRY:  
  86.   ; Сделать адресуемыми данные
  87.   mov   AX, SELECTOR_DATA   ; Селектор сегмента данных
  88.   mov   DS, AX
  89.   mov   AX, SELECTOR_FLAT
  90.   mov   ES, AX
  91.   ; Сделать адресуемыми стек
  92.   mov   AX, SELECTOR_STACK  ; Селектор сегмента стека
  93.   mov   EBX, STACK_SIZE
  94.   mov   SS, AX
  95.   mov   ESP, EBX
  96.  
  97.   ; Защищенный режим
  98.   mov   ESI, offset msg_prot        ; DS:ESI - сообщение
  99.   mov   EDI, 0B8000h                ; ES:EDI - видеопамять
  100.   mov   ECX, msg_prot_size          ; ECX - длина
  101.   rep movsb                         ; вывод на экран
  102.   ; Таймер
  103.   mov   ESI, offset msg_time        ; DS:ESI - сообщение
  104.   mov   EDI, 0B80A0h                ; ES:EDI - видеопамять
  105.   mov   ECX, msg_time_size          ; ECX - длина
  106.   rep movsb                         ; вывод на экран
  107.   ; Память
  108.   mov   ESI, offset msg_mem         ; DS:ESI - сообщение
  109.   mov   EDI, 0B8140h                ; ES:EDI - видеопамять
  110.   mov   ECX, msg_mem_size           ; ECX - длина
  111.   rep movsb                         ; вывод на экран
  112.    
  113.   ; Разрешить обработку прерываний
  114.   sti
  115.  
  116.   ; Вывести доступную память
  117.   call  show_memory
  118.  
  119.   ; Бесконечный цикл
  120.   work:
  121.     test escape, 1
  122.   jz  work
  123.  
  124.   ; запрещаем прерывания, всё по той же причине
  125.   ;при этом немаскируемые уже запрещены, их не трогаем
  126.   cli
  127.  
  128.   ; ВОЗВРАЩЕНИЕ В РЕАЛЬНЫЙ РЕЖИМ
  129.   ; Сформироывть и загрузить дескрипторы для реального режима
  130.   mov   gdt_flat.lim, 0FFFFh
  131.   mov   gdt_data.lim, 0FFFFh
  132.   mov   gdt_code_16.lim, 0FFFFh
  133.   mov   gdt_code_32.lim, 0FFFFh
  134.   mov   gdt_stack.lim, 0FFFFh
  135.   push  DS ; Загрузить сегмент данных из теневого регистра
  136.   pop   DS
  137.   push  SS ; Стек
  138.   pop   SS
  139.   push  ES ; Видеопамять
  140.   pop   ES
  141.  
  142. ; Загрузить регистр сегмента команд при помощи дальнего перехода
  143. db  0EAh
  144. dd  offset REAL_MODE_RETURN
  145. dw  SELECTOR_CODE_16           
  146.  
  147.  
  148.        
  149. ; Создание символа из цифры
  150. create_number macro
  151. local number1
  152.   cmp   DL, 10
  153.   jl    number1
  154.   add   DL, 'A' - '0' - 10
  155. number1:
  156.   add   DL, '0'
  157. endm
  158.  
  159.  
  160.  
  161. ; Напечатать значение в eax в видеобуфер со смещение из ebp
  162. my_print_eax macro
  163. local prcyc1
  164.   ; Занести регистры в стек
  165.   push  EBP
  166.   push  ECX
  167.   push  EDX
  168.    
  169.   ; Инициализация регистров
  170.   mov   ECX, 8          ; количество символов, которые распечатаем
  171.   add   EBP, 0B8010h    ; печать числа с конца
  172.   mov   DH, 70h         ; стиль символа
  173.  
  174.   ; Вывод в видеобуфер
  175. prcyc1:
  176.   mov   DL, AL          ; кладём в ДЛ текущее значение АЛ (самый младший байт ЕАХ)
  177.   and   DL, 0Fh         ; оставляем от него одно 16ричное число (последняя цифра)
  178.   create_number 0       ; превращаем это число в символ
  179.   mov   ES:[EBP], DX    ; заносим al в видеобуфер
  180.   ror   EAX, 4          ; циклически двигаем биты в ЕАХ
  181.   sub   EBP, 2          ; смещаемся на один символ влево (предыдущая цифра в ЕАХ)
  182. loop  prcyc1            ; цикл по ecx
  183.    
  184.   ; Восстановить регистры из стека
  185.   pop   EBP
  186.   pop   EDX
  187.   pop   ECX
  188. endm
  189.  
  190.  
  191.  
  192. ;заглушка для обработки первых 32х исключений
  193. dummy:
  194.   nop
  195. iretd
  196.  
  197.  
  198.  
  199. ; Обработчик прерывания от таймера
  200. new_08:
  201.   ; Занести регистры в стек
  202.   push  EAX
  203.   push  EBP
  204.   push  ECX
  205.   push  EDX
  206.  
  207.   ; Считаем смещение для текста
  208.   mov   EBP, 0B8000h
  209.   add   EBP, 0A0h
  210.   ; Вывести на экран текст
  211.   mov   ESI, offset msg_time    ; DS:ESI - сообщение
  212.   mov   EDI, EBP                ; ES:EDI - видеопамять
  213.   mov   ECX, msg_time_size      ; ECX - длина
  214.   rep movsb  
  215.  
  216.   ; Указываем смещение видеобуфера
  217.   mov   EBP, 0A0h
  218.   add   EBP, msg_mem_size
  219.   ; Вывести на экран eax - время
  220.   mov   EAX, time_08
  221.   my_print_eax 0
  222.  
  223.   ; Инкремент счетчика времени
  224.   inc time_08
  225.  
  226.   ; Отправить команду End of Interrupt ведущему контроллеру прерываний
  227.   mov   AL, 20h
  228.   out   20h, AL
  229.  
  230.   ; Востановить регисты
  231.   pop   EDX
  232.   pop   ECX
  233.   pop   EBP
  234.   pop   EAX
  235.  
  236.   ; Выход из прерывания
  237. iretd
  238.  
  239.    
  240.        
  241. ; Обработчик прерывания клавиатуры
  242. new_09:
  243.   ; Занести регистры в стек
  244.   push  EAX
  245.   push  EBX
  246.   push  EDX
  247.   push  EBP
  248.  
  249.   ; прочитать скан-код нажатой клавиши,
  250.   in    AL, 60h
  251.   ; нажат Enter - 1Ch
  252.   cmp   AL, 1Ch
  253.   je    enter_pressed  
  254.   ; если он больше, чем максимальный, пропустить  
  255.   cmp   AL, 39h
  256.   ja    skip_translate
  257.  
  258.   ; DS:EBX - таблица для перевода скан-кода в ASCII
  259.   mov   EBX, offset scan2ascii
  260.   xlatb  
  261.  
  262.   ; Считаем смещение для выворда
  263.   mov   EBP, 0B8000h
  264.   add   EBP, input_pos
  265.  
  266.   ; Нажат backspace
  267.   cmp   AL, 8
  268.   je    bs_pressed
  269.  
  270.   ; Вывод символа
  271.   mov   AH, 0Bh ; color style
  272.   mov   ES:[EBP], AX
  273.   add   dword ptr input_pos, 2
  274. init:
  275.   mov   AL, 0Eh
  276.   mov   AL, BH
  277.   out   DX, AX  
  278.   mov   AL, 0Fh  
  279.   mov   AH, BL
  280.   out   DX, AX
  281.   jmp   short skip_translate
  282. bs_pressed:                
  283.   ; Вывести пробел в предыдущей позиции
  284.   sub   dword ptr input_pos, 2   
  285.   mov   AL, ' '
  286.   sub   EBP, 2
  287.   mov   ES:[EBP], AX
  288.   jmp   init
  289.  
  290. skip_translate:
  291.   ; Разрешить работу клавиатуры
  292.   in    AL, 61h
  293.   or    AL, 80h
  294.   out   61h, AL
  295.  
  296.   ; Послать EOI контроллеру прерываний
  297.   mov   AL, 20h
  298.   out   20h, AL
  299.  
  300.   ; Востановить регисты
  301.   pop   EBP
  302.   pop   EDX
  303.   pop   EBX
  304.   pop   EAX
  305.  
  306.   ; Выход из прерывания
  307.   iretd
  308.  
  309. enter_pressed:
  310.   mov   escape, 1
  311. jmp skip_translate
  312.  
  313.  
  314.  
  315. ; Подсчитать количество свободной памяти
  316. show_memory proc
  317.   ; Занести сегмент данных в стек
  318.   push  DS
  319.   ; Занести начальные значения
  320.   mov   AX, SELECTOR_FLAT   ;кладем в него сегмент на 4 ГБ - все доступное виртуальное АП
  321.   mov   DS, AX
  322.   mov   EBX, 100001h        ; пропускаем первый мегабайт оного сегмента
  323.                             ; мегабайт пропускаем потому, что в противном случае может произойти попытка редактирования процедуры собственного кода
  324.   mov   DL, 10101010b       ; байт записи                
  325.   mov   ECX, 0FFFFFFFFh     ; количество оставшейся памяти (до превышения лимита в 4ГБ) - чтобы не было переполнения
  326.    
  327.   ; Цикл подсчета памяти
  328. check:
  329.   mov   DH, DS:[EBX]    ;сохраняем в DH текущее значение по некоторому байту памяти
  330.   mov   DS:[EBX], DL    ;кладём некоторое значение в этот байт
  331.   cmp   DS:[EBX], DL    ;проверяем - считается обратно то же значение
  332.   jnz   end_of_memory   ;если не совпало, то мы достигли дна (конец памяти)
  333.   mov   DS:[EBX], DH    ;иначе восстанавливаем значение в памяти
  334.   inc   EBX
  335. loop  check
  336.  
  337.   ; Завершить подсчет памяти
  338. end_of_memory:
  339.   xor   EDX, EDX
  340.   mov   EAX, EBX        ;в EBX лежит количество посчитанной памяти в байтах; кладём его в EAX,
  341.   mov   EBX, 100000h    ;делим на 1 Мб, чтобы получить результат в мегабайтах
  342.   div   EBX
  343.   ; Восстанавливаем сегмент данных
  344.   pop   DS
  345.  
  346.   ; Вывести на экран текст
  347.   mov   ESI, offset msg_mem     ; DS:ESI - сообщение
  348.   mov   EDI, 0B8140h            ; ES:EDI - видеопамять
  349.   mov   ECX, msg_mem_size       ; ECX - длина
  350.   rep movsb  
  351.  
  352.   ; Вывести количество доступной памяти
  353.   push  EBP
  354.   mov   EBP, 140h
  355.   add   EBP, msg_mem_size
  356.   my_print_eax 0
  357.   pop   EBP
  358.  
  359.   ret
  360. show_memory endp
  361.  
  362. PROTECTED_MODE_SG_size = $-GDT
  363. PROTECTED_MODE_SG   ENDS
  364.  
  365.  
  366.  
  367.  
  368. ; СЕГМЕНТ СТЕКА
  369. STACK_SG SEGMENT PARA STACK 'STACK' USE32
  370.     stack_main db 100h dup (?)
  371.     STACK_SIZE = $-stack_main
  372. STACK_SG ENDS
  373.  
  374.  
  375.  
  376.  
  377. ; СЕГМЕНТ КОДА РЕАЛЬНОГО РЕЖИМА
  378. REAL_MODE_SG SEGMENT PARA PUBLIC 'CODE' USE16
  379.   ASSUME CS:REAL_MODE_SG, DS:PROTECTED_MODE_SG, SS:STACK_SG
  380.  
  381. ; MAIN
  382. main proc
  383.   ; Очистить экран
  384.   mov   AX, 3h
  385.   int   10h
  386.   ; Настроить сегмент данных
  387.   xor   EAX, EAX
  388.   mov   AX, PROTECTED_MODE_SG ; Загрузим в DS сегментный
  389.   mov   DS, AX   ; адресс сегмента данных
  390.  
  391.   ; Вычислить 32-битовый линейный адрес для сегмента команд и загрузить его в дескриптор GDT
  392.   xor   EAX, EAX
  393.   mov   AX, REAL_MODE_SG
  394.   shl   EAX, 4
  395.   mov   word ptr gdt_code_16.base_l, AX
  396.   shr   EAX, 16
  397.   mov   byte ptr gdt_code_16.base_m, AL
  398.   mov   byte ptr gdt_code_16.base_h, AH
  399.                
  400.   ; Вычислить 32-битовый линейный адрес для остальных сегментов
  401.   mov   AX, PROTECTED_MODE_SG
  402.   shl   EAX, 4
  403.   mov   word ptr gdt_code_32.base_l, AX            
  404.   mov   word ptr gdt_stack.base_l, AX
  405.   mov   word ptr gdt_data.base_l, AX
  406.   shr   EAX, 16
  407.   mov   byte ptr gdt_code_32.base_m, AL
  408.   mov   byte ptr gdt_stack.base_m, AL
  409.   mov   byte ptr gdt_data.base_m, AL
  410.   mov   byte ptr gdt_code_32.base_h, AH
  411.   mov   byte ptr gdt_stack.base_h, AH
  412.   mov   byte ptr gdt_data.base_h, AH
  413.  
  414.   ; Подготовить псевдодескриптора и загрузить его в GDTR (6 байт: 0-1 граница, 2-5 линейный адрес )
  415.   mov   AX, PROTECTED_MODE_SG
  416.   shl   EAX, 4
  417.   add   EAX, offset gdt ; в eax будет полный линейный адрес GDT (адрес сегмента + смещение GDT относительно него)
  418.   mov   dword ptr gdtr + 2, EAX ; кладём полный линейный адрес в младшие 4 байта переменной gdtr
  419.   mov   word ptr gdtr, gdt_size - 1; в старшие 2 байта заносим размер gdt, из-за определения gdt_size (через $) настоящий размер на 1 байт меньше
  420.   lgdt  fword ptr gdtr
  421.  
  422.   ; Аналогично IDTR
  423.   mov   AX, PROTECTED_MODE_SG
  424.   shl   EAX, 4
  425.   add   eax,offset idt
  426.   mov   dword ptr idtr + 2,eax
  427.   mov   word ptr idtr, idt_size - 1
  428.   lidt  fword ptr idtr
  429.  
  430.   ; Заполним смещение в дескрипторах прерываний
  431.   mov   EAX, offset new_08
  432.   mov   int08.offs_l, AX
  433.   shr   EAX, 16
  434.   mov   int08.offs_h, AX
  435.   mov   EAX, offset new_09
  436.   mov   int09.offs_l, AX
  437.   shr   EAX, 16
  438.   mov   int09.offs_h, AX
  439.  
  440.   ; Сохранить маски прерываний контроллеров
  441.   in    AL, 21h    ; ведущий, 21h - номер шины, in на неё даст нам набор масок (флагов)
  442.   mov   master, AL
  443.   in    AL, 0A1h
  444.   mov   slave, AL ; ведомый
  445.    
  446.   ; Перепрограммируем ведущий контроллер
  447.   mov   AL, 11h ; СЛИ1
  448.   out   20h, AL
  449.   mov   AL, 32  ; СЛИ2 - бозовый вектор
  450.   out   21h, AL
  451.   mov   AL, 4   ; СЛИ3
  452.   out   21h, AL
  453.   mov   AL, 1   ; СЛИ4 - EOF
  454.   out   21h, AL
  455.   ; Запретим все прерывания в ведущем контроллере, кроме IRQ0 (таймер) и IRQ1(клавиатура)
  456.   mov   AL, 0FCh ; 0 - таймер, 1 - клавиатура
  457.   out   21h, AL
  458.        
  459.   ; Запретим все прерывания в ведомом контроллере, иначе может прийти прерывание, для которого у нас нет обработчика
  460.   mov   AL, 0FFh
  461.   out   0A1h, AL
  462.        
  463.   ; Если мы собираемся работать с 32-битной памятью, стоит открыть A20
  464.   ; А20 - линия ("шина"), через которую осуществляется доступ ко всей памяти за пределами первого мегабайта
  465.   ; если мы собираемся работать с 32-битной памятью, стоит открыть A20
  466.   in    AL, 92h  ; получить набор флагов
  467.   or    AL, 2    ; добавить единичку во 2 бите
  468.   out   92h, AL  ; отправить обратно
  469.        
  470.   ; Запретить маскируемые прерывания
  471.   cli
  472.   ; Запретить немаскируемые прерывания
  473.   in    AL, 70h
  474.   or    AL, 80h
  475.   out   70h, AL
  476.        
  477.   ; Переход в защищенный режим
  478.   mov   EAX, CR0
  479.   or    EAX, 1      ; установим бит защищенного режима
  480.   mov   CR0, EAX
  481.  
  482.   ; Переходив в сегмент защищенного режима, устанавливаем теневой регистр CS
  483.   db    66h
  484.   db    0EAh                        ; far jmp
  485.   dd    offset PROTECTED_MODE_ENTRY ; смещение
  486.   dw    SELECTOR_CODE_32            ; селектр сегмента команд
  487.        
  488. REAL_MODE_RETURN:  
  489.  
  490.   ; Переключить режим процессора на реальный режим
  491.   mov   EAX, CR0
  492.   and   EAX, 0FFFFFFFEh
  493.   mov   CR0, EAX
  494.  
  495.   ; Загрузить сегментный адрес в CS
  496.   db    0EAh
  497.   dw    $+4             ; смещение - след строка
  498.   dw    REAL_MODE_SG    ; сегмент
  499.  
  500.   ; Восстановить вычислительную среду реального режима
  501.   mov   AX, PROTECTED_MODE_SG
  502.   mov   DS, AX
  503.   mov   AX, SELECTOR_STACK
  504.   mov   EBX, STACK_SIZE
  505.   mov   ESP, EBX
  506.   mov   AX, STACK_SG
  507.   mov   SS, AX
  508.        
  509.   ; Восстановить ведущий контроллер
  510.   mov   AL, 11h
  511.   out   20h, AL
  512.   mov   AL, 8
  513.   out   21h, AL
  514.   mov   AL, 4
  515.   out   21h, AL
  516.   mov   AL, 1
  517.   out   21h, AL
  518.  
  519.   ; Восстановить маски прерываний
  520.   mov   AL, master
  521.   out   21h, AL
  522.   mov   AL, slave
  523.   out   0A1h, AL
  524.        
  525.   ; Загрузить таблицу дескрипторов прерываний реального режима    
  526.   lidt  fword ptr idtr_real
  527.        
  528.   ; Разрешит немаскируемые прерывания
  529.   in    al,70h
  530.   and   al,07FH
  531.   out   70h,al
  532.   ; Разрешить аппаратные прерывания
  533.   sti  
  534.        
  535.   ; Очистить экран
  536.   mov   AX, 3
  537.   int   10h
  538.   ; Вывод сообщения о переходе в реальный режим
  539.   mov   AH, 09h
  540.   mov   DX, offset msg
  541.   int   21h
  542.   ; Завершить программу
  543.   mov   AX, 4C00h
  544.   int   21h
  545.  
  546. REAL_MODE_SG_SIZE = $-main
  547. REAL_MODE_SG ENDS
  548.     END main
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement