Bladeren bron

实现数字打印 put_int

simon 11 maanden geleden
bovenliggende
commit
6155b265a6
7 gewijzigde bestanden met toevoegingen van 234 en 3 verwijderingen
  1. 1 1
      boot/loader.S
  2. 2 2
      build.sh
  3. 147 0
      doc/about_0x7c00.txt
  4. BIN
      doc/mbr.png
  5. 9 0
      kernel/main.c
  6. 66 0
      lib/kernel/print.S
  7. 9 0
      lib/kernel/print.h

+ 1 - 1
boot/loader.S

@@ -396,7 +396,7 @@ kernel_init:
         call mem_cpy                      ; 调用 mem_cpy 完成段自制
         add esp, 12                       ; 清理栈中压入的三个参数
 
-    .PTNULL 
+    .PTNULL: 
         add ebx, edx                      ; edx 为 program header 大小,此时 ebx 指向下一个 program header  
         loop .each_segment
     ret

+ 2 - 2
build.sh

@@ -19,5 +19,5 @@ echo " 2. Writing kernel to disk..."
 dd if=kernel/kernel.bin of=hd30M.img bs=512 count=200 seek=9 conv=notrunc
 echo "Disk image created successfully."
 
-# echo "Now starting bochs ..."
-# bochs -f bochsrc.txt
+echo "Now starting bochs ..."
+bochs -f bochsrc -q

+ 147 - 0
doc/about_0x7c00.txt

@@ -0,0 +1,147 @@
+* The mysteries arround "0x7C00" in x86 architecture bios bootloader
+
+Do you know "0x7C00", a magic number, in x86 assembler programming ?
+"0x7C00" is the memory address which BIOS loads MBR(Master Boot Record, a first sector in hdd/fdd) into. OS or bootloader developer must assume that their assembler codes are loaded and start from 0x7C00.
+
+But... ''1st'' , you may wonder.
+>"I read all of Intel x86(32bit) programmers manual, but did not found the magic number 0x7C00."
+
+Yes. ''0x7C00 is NOT related to x86 CPU'' . It's natural that you couldn't find out it in cpu specifications from intel. Then, you wonder, "Who decided it ?"
+
+''2nd'' , you may wonder:
+>"0x7C00 is 32KiB - 1024B at decimal number. What's this number means ?"
+
+Anyone decided it. But, why he/she decided such a halfway address?
+
+Hum...There're TWO questions(mysteries) arround the magic number "0x7C00".
++ Who decided "0x7C00" ?
++ What "0x7C00 = 32KiB - 1024B" means ?
+
+Okay, let's dive into the secret of BIOS for "IBM PC 5150", ancestor of modern x86(32bit) PCs, with me...!!
+
+#more||
+
+* "0x7C00" First appeared in IBM PC 5150 ROM BIOS INT 19h handler.
+
+Wandering arround the history of x86 IBM Compatible PC, you know  ''IBM PC 5150''  is the ancestor of modern x86(32bit) IBM PC/AT Compatible PCs.
+This PC was released at 1981 August, with Intel 8088(16bit) and 16KiB RAM(for minimum memory model). BIOS and Microsoft BASIC was stored in ROM.
+
+When power on, BIOS processes "POST"(Power On Self Test) procedure, and after,  ''call INT 19h'' .
+In INT 19h handler, BIOS checks that PC has any of floppy/hard/fixed diskette or not have.
+If PC has any of available diskkete, BIOS loads a first sector(512B) of diskette into 0x7C00.
+
+Now, you understand why you couldn't find out this magic number in x86 documents.  ''This magic number belongs to BIOS specification.''
+
+* The origin of 0x7C00
+
+Stories surrounding IBM PC DOS, Microsoft, and SCP's 86-DOS are famous stories. See: [["A Short History of MS-DOS">http://www.patersontech.com/dos/Byte/History.html]].
+
+SCP's "86-DOS"(at 1980) is the reference OS for IBM PC DOS 1.0.
+86-DOS(early called "QDOS") is CP/M compatible OS for 8086/8088 cpu. At 1979, Digital Research Inc didn't have developed CP/M for 8086/8088 cpu yet.
+
+SCP sold two S-100 bus board, one is 8086 CPU board, two is "CPU Monitor" rom board.
+"CPU Monitor" program provided bootloader and debugger.  ''This "CPU Monitor" bootloader loaded MBR into "0x200", NOT "0x7C00"'' . In 1981, IBM PC DOS was the NEXT CP/M like OS for 8086/8088.
+
+So, I told you that "0x7C00  ''FIRST appeared''  in IBM PC 5150 ROM BIOS".
+Previous one, SCP's CPU Monitor bootloader loads into 0x200, not 0x7C00.
+
+** Why that CPU Monitor's bootloader loeded MBR into "0x200" ?
+
+There're THREE reasons about "0x200".
++ 8086 Interrupts Vector use 0x0 - 0x3FF.
++ 86-DOS was loaded from 0x400.
++ 86-DOS didn't use interrupts vectors between 0x200 - 0x3FF.
+
+These reasons mean 0x200 - 0x3FF needed to be reserved and couldn't be in the way of an OS, no matter where 86-DOS or user application wanted to load.
+
+So Tim Paterson (86-DOS developer) chose 0x200 for MBR load address.
+
+* Q:Who decided "0x7C00" ? - A: IBM PC 5150 BIOS Developer Team.
+
+"0x7C00" was decided by IBM PC 5150 BIOS developer team (Dr. David Bradley).
+As mentioned above, this magic number was born at 1981 and "IBM PC/AT Compat" PC/BIOS vendors did not change this value for BIOS and OS's backward compatibility.
+
+Not Intel(8086/8088 vendor) nor Microsoft(OS vendor) decided it.
+
+* Q:What "0x7C00 = 32KiB - 1024B" means ? A: Affected by OS requirements and CPU memory layout.
+
+IBM PC 5150 minimum memory model had only 16KiB RAM. So, you may have a question.
+>"Could minimum memory model (16KiB) load OS from diskette ? BIOS loads MBR into 32KiB - 1024B address, but physical RAM is not enough..."
+
+No, that case was  ''out of consideration'' . One of IBM PC 5150 ROM BIOS Developer Team Members, Dr. David Bradley says:
+>"DOS 1.0 required a minimum of 32KB, so we weren't concerned about attempting a boot in 16KB."
+(Note: DOS 1.0 required 16KiB minimum ? or 32KiB ? I couldn't find out which correct. But, at least, in 1981's early BIOS development, they supposed that 32KiB is DOS minimum requirements.)
+
+BIOS developer team decided 0x7C00 because:
++ They wanted to leave as much room as possible for the OS to load itself within the 32KiB.
++ 8086/8088 used 0x0 - 0x3FF for interrupts vector, and BIOS data area was after it.
++ The boot sector was 512 bytes, and stack/data area for boot program needed more 512 bytes.
++ So, 0x7C00, the last 1024B of 32KiB was chosen.
+
+Once OS loaded and started, boot sector is never used until power reset. So, OS and application can use the last 1024B of 32KiB freely.
+
+After OS loaded, memory layout will be:
+#pre||>
++--------------------- 0x0
+| Interrupts vectors
++--------------------- 0x400
+| BIOS data area
++--------------------- 0x5??
+| OS load area
++--------------------- 0x7C00
+| Boot sector
++--------------------- 0x7E00
+| Boot data/stack
++--------------------- 0x7FFF
+| (not used)
++--------------------- (...)
+||<
+
+That are the origin and reasons of "0x7C00", the magic number survived for about three decades in PC/AT Compat BIOS INT 19h handler.
+
+* References
+
+86-DOS related:
+- "8086 Monitor Instruction Manual"(MON 86 - V1.4)
+- "86-DOS(TM) User's Manual Version 0.3"
+- "86-DOS(TM) Programmer's Manual Version 0.3"
+- "86-DOS(TM) Instruction Manual Version ??"
+
+IBM PC 5150 related:
+- "IBM Personal Computer Hardware Reference Library", "Technical Reference" (IBM Personal Computer Technical Reference manual)
+- "IBM Personal Computer XT Hardware Reference Library", "Technical Reference" (IBM Personal Computer XT Technical Reference manual)
+
+Intel 8086/8088 data sheets:
+- "8086 16-BIT HMOS MICROPROCESSOR"
+- "M80C86/M80C86-2 16-BIT CHMOS MICROPROCESSOR"
+- "8088 8-BIT HMOS MICROPROCESSOR"
+
+CP/M related:
+- The Unofficial CP/M Web Site
+-- http://www.cpm.z80.de/
+- CP/M Internals : Oscar Vermeulen Personal Web Site
+-- http://www.dcast.vbox.co.uk/cpm.html
+- Digital Research - CP/M
+-- http://www.digitalresearch.biz/CPM.HTM
+- CP/M Main Page
+-- http://www.seasip.demon.co.uk/Cpm/
+
+86-DOS related:
+- Origins of DOS - Paterson Technology
+-- http://www.patersontech.com/dos/
+- 86-DOS Resource Website
+-- http://www.86dos.org/index.htm
+- DosMan Drivel
+-- http://dosmandrivel.blogspot.com/
+
+And all related Wikipedia pages.
+
+* Special Thanks To...
+
+Special Thanks To:
+- Tim Peterson
+- David Bradley
+
+for japanese article, see:
+"Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版)"
+https://www.glamenv-septzen.net/view/614

BIN
doc/mbr.png


+ 9 - 0
kernel/main.c

@@ -3,6 +3,15 @@
 int main(void)
 {
     put_str("I am kernel\n");
+    put_int(0);
+    put_char('\n');
+    put_int(9);
+    put_char('\n');
+    put_int(0x00021a3f);
+    put_char('\n');
+    put_int(0x12345678);
+    put_char('\n');
+    put_int(0x00000);
     while (1)
         ;
     return 0;

+ 66 - 0
lib/kernel/print.S

@@ -18,9 +18,13 @@ LF equ 0xa
 BACKSPACE equ 0x8
 
 [bits 32]
+section .data 
+put_int_buffer  dq 0     ; 定义 8 字节缓冲区用于数字到字符的转换
+
 section .text 
 global put_char ; 导出为全局符号
 global put_str  
+global put_int
 ;------------------------ put_char -----------------------------------------------
 ; 功能描述: 把栈中的 1 个字符写入光标所在处
 ;---------------------------------------------------------------------------------
@@ -158,3 +162,65 @@ put_str:
     pop ecx
     pop ebx
     ret
+
+;------------------------ put_int ------------------------------------------------
+; 功能描述: 将小端字节序的数字变成对应 ASCII 后,倒置
+; 输入:栈中参数为待打印的数字
+; 输出:在屏幕在打印十六进制数字,并不会打印前缀 0X,如打印十进制 15 时,会直接打印 f
+;---------------------------------------------------------------------------------
+put_int:
+    pushad 
+    mov ebp, esp 
+    mov eax, [ebp+4*9]  ; call 的返回地址占 4 字节+pushad 的占 8 个 4 字节
+    mov edx, eax
+    mov edi, 7          ; 指定在 put_int_buffer 中初始的偏移量
+    mov ecx, 8          ; 32 位数字中,十六进制数字的位数是 8 个
+    mov ebx, put_int_buffer
+
+    ; 将 32 位数字按照十六进制的形式从低位到高位逐个处理
+    ; 共处理 8 个数
+.16based_4bits:         ; 每 4 位二进制是十六进制数字的 1 位
+    and edx, 0xF        ; 取低 4 位
+    cmp edx, 9          ; 数字 0~9 和 a~f 需要分别处理成对应的字符
+    jg .is_A2F
+    add edx, '0'        ; ASCII 码是 8 位大小。add 求和后,edx 低 8 位有效
+    jmp .store
+.is_A2F:
+    sub edx, 10         ; A~F 减去 10 所得到的差,再加上字符 A 的 ASCII 码,便是
+    add edx, 'A'        ; A~F 对应的 ASCII 码
+
+    ; 将第一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区 put_int_buffer
+    ; 高位字符放在低地址,低位字符放在高位地址
+.store:
+    ; 此时 dl 中是数字对应的字符的 ASCII 码
+    mov [ebx+edi], dl 
+    dec edi 
+    shr eax, 4
+    mov edx, eax
+    loop .16based_4bits
+
+    ; 现在 put_int_buffer 中已全是字符,打印之前,把高位连续的字符去掉,如把字符 000123 变成 123
+.ready_to_print:
+    inc edi               ; 此时 edi 退减为-1(0xffffffff),加 1 使其为 0
+.skip_prefix_0:
+    cmp edi, 8
+    je .full0             ; 若已经比较第 9 个字符了,表示待打印的字符串全为 0
+.go_on_skip:
+    mov cl, [ebx+edi] 
+    inc edi
+    cmp cl, '0'
+    je .skip_prefix_0
+    dec edi               ; 若当前字符不为'0',要使 edi 恢复指向当前字符
+    jmp .put_each_num
+.full0:
+    mov cl, '0'           ; 输入的数字全 0 时,只打印 0
+.put_each_num:
+    push ecx              ; 此时 cl 中为可打印的字符
+    call put_char
+    add esp, 4
+    inc edi 
+    mov cl, [ebx+edi]   ; 获取下一个字符到 cl
+    cmp edi, 8
+    jl .put_each_num
+    popad
+    ret

+ 9 - 0
lib/kernel/print.h

@@ -17,4 +17,13 @@ void put_char(uint8_t char_ascii);
  * @retval None
  */
 void put_str(char *message);
+
+/**
+ * @brief  打印整数
+ * @note   以十六进制形式打印整数
+ * @param  num: 数字
+ * @retval None
+ */
+void put_int(uint32_t num);
+
 #endif // __LIB_KERNEL_PRINT_H