Notes on the implementation of an operating system: using the size of memory to set the number of page tables

This article is not long, mainly the code at the end is long. If you are interested in the code, you can read it. If you are not interested, you can read the content of the article.

The formula for setting the number of page tables with the size of memory is: number of page tables = memory size ÷ 4MB.

Why is that so? First of all, the size of a page in 80386 is 4KB, and the page is found through the page table, and there are 1024 pages in a page table, so the physical memory corresponding to a page table is 4MB, so the number of page tables = memory size ÷ 4MB, so we can use memory diligently.

The code for calculating the number of page tables is as follows:

SetupPaging:
	; Calculate how much should be initialized according to the memory size PDE And how many pages
	xor	edx, edx
	mov	eax, [dwMemSize]
	mov	ebx, 400000h	; 400000h = 4M = 4096 * 1024, Memory size of a page table
	div	ebx
	mov	ecx, eax	; here ecx Is the number of page tables, i.e PDE Should be
	test	edx, edx
	jz	.no_remainder
	inc	ecx		; Add a page table if the remainder is not 0

The value of memory size is saved in [dwMemSize], and obtaining the memory size is the main part of the program.

This program obtains the memory size by calling the 15h interrupt. Before calling the 15h interrupt, the following registers need to be filled:
① eax: int 15h can accomplish a lot of work, mainly determined by the value of ax. If we want to get memory information, we need to assign ax to 0E820h.
(2) ebx: place "continuation value". Ebx must be 0 on the first call.
③ es:di: point to an Address Range Descriptor Structure, ARDS (Address Range Descriptor Structure), which will be filled by BIOS.
④ the size of the address range descriptor structure pointed to by ecx: es:di, in bytes. No matter what structure es:di points to, BIOS will fill up to ecx bytes. However, in general, no matter how large the ecx is, BIOS only fills 20 bytes. Some BIOS ignore the value of ecx and always fills 20 bytes.
⑤ edx 0534D4150h ('SMAP ') -- the BIOS will use this flag to verify the system image information that the caller will request, which will be placed in the structure pointed to by es:di by the BIOS.

After the interrupt call, the results are stored in the following registers.
① CF: CF=0 means there is no error, otherwise there is an error.
      ②eax: 0534D4150h('SMAP').
③ es:di: the returned address range descriptor structure pointer is the same as the input value.
④ ecx: the number of bytes filled in the address range descriptor by BIOS. The minimum value returned by BIOS is 20 bytes.
⑤ ebx: the subsequent value needed to wait until the next address descriptor is placed here. The actual situation of this value depends on the implementation of the specific BIOS. The caller does not need to care about its specific form, just put it in the ebx unchanged in the next iteration, and the next address range descriptor can be obtained through it. If its value is 0 and CF has no carry, it is the last address range descriptor.

The introduction of the above register values may seem boring. It doesn't matter. What you need to understand to get memory is not related to the above. The above is mainly preparation work, mainly understanding the following.

Address Range Descriptor Structure:

deviation Name Significance
0 BaseAddrLow Lower 32 bits of base address
4 BaseAddrHigh Top 32 bits of base address
8 LengthLow Lower 32 bits of length (bytes)
12 LengthHigh High 32 bits of length (bytes)
16 Type Address type of this address range

Where, the value and meaning of Type are as follows:

Value Name Significance
1 AddressRangeMemory This memory segment is a segment of RAM that can be used by the OS
2 AddressRangeReserved This address segment is in use or reserved by the system, so it must not be used by the OS
Other Undefined Reserved. For future use, any other value must be considered as AddressRangeReserved by the OS

When ax=0E820h, int 15h is called to get not only the memory size, but also some descriptions of different memory segments. Moreover, these descriptions are stored in a buffer. So before we call int 15h, we have to have a buffer. We can use the same buffer every time we get a memory description, and then process the data in the buffer. We can also put the data in different locations, such as a piece of continuous memory, and then read it when we want to process them. The latter may be more convenient, so a 256 byte buffer is defined here, which can store up to 12 20 byte structures.

_MemChkBuf: times 256 db 0
	; Get the number of memory
	mov	ebx, 0
	mov	di, _MemChkBuf
.loop:
	mov	eax, 0E820h
	mov	ecx, 20
	mov	edx, 0534D4150h
	int	15h
	jc	LABEL_MEM_CHK_FAIL
	add	di, 20
	inc	dword [_dwMCRNumber]
	cmp	ebx, 0
	jne	.loop
	jmp	LABEL_MEM_CHK_OK
LABEL_MEM_CHK_FAIL:
	mov	dword [_dwMCRNumber], 0
LABEL_MEM_CHK_OK:

The code uses a loop that ends once CF is set or ebx is zero. Before the start of the first cycle, eax is 0000E820h, ebx is 0, ecx is 20, edx is 0534D4150h, es:di points to the beginning of_MemChkBuf. During each cycle, the value of register di will increase by 20 bytes. In addition, the values of eax, ecx and edx do not change, and we ignore the values of ebx. At the same time, we add 1 to the value of dwMCRNumber in each cycle, so that its value will be the number of cycles at the end of the cycle, and also the number of address range descriptor structures.

It is mainly the 32-bit code in the following protection mode, adding the process of displaying memory information (including the code of how the memory size comes from).

DispMemSize:
	push	esi
	push	edi
	push	ecx

	mov	esi, MemChkBuf
	mov	ecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++)//Get one ARDS at a time
.loop:				  ;{
	mov	edx, 5		  ;  for(int j=0;j<5;j++) //Get members in ARDS one at a time
	mov	edi, ARDStruct	  ;  {//Baseaddrlow, baseaddrlow, lengthlow are displayed in turn,
.1:				  ;             LengthHigh,Type
	push	dword [esi]	  ;
	call	DispInt		  ;    DispInt(MemChkBuf[j*4]); //Show a member
	pop	eax		  ;
	stosd			  ;    ARDStruct[j*4] = MemChkBuf[j*4];
	add	esi, 4		  ;
	dec	edx		  ;
	cmp	edx, 0		  ;
	jnz	.1		  ;  }
	call	DispReturn	  ;  printf("\n");
	cmp	dword [dwType], 1 ;  if(Type == AddressRangeMemory)
	jne	.2		  ;  {
	mov	eax, [dwBaseAddrLow];
	add	eax, [dwLengthLow];
	cmp	eax, [dwMemSize]  ;    if(BaseAddrLow + LengthLow > MemSize)
	jb	.2		  ;
	mov	[dwMemSize], eax  ;    MemSize = BaseAddrLow + LengthLow;
.2:				  ;  }
	loop	.loop		  ;}
				  ;
	call	DispReturn	  ;printf("\n");
	push	szRAMSize	  ;
	call	DispStr		  ;printf("RAM size:");
	add	esp, 4		  ;
				  ;
	push	dword [dwMemSize] ;
	call	DispInt		  ;DispInt(MemSize);
	add	esp, 4		  ;

	pop	ecx
	pop	edi
	pop	esi
	ret

What is stored in the address [dwMemSize] is the size of memory. If baseaddrlow + lengthlow > memsize, then MemSize = BaseAddrLow + LengthLow.
There are two questions here. One is why to judge baseaddrlow + lengthlow > memsize. The other is why MemSize = BaseAddrLow + LengthLow.
Judging baseaddrlow + lengthlow > memsize is due to the two sentences in front of the code:
      _dwMemSize: dd 0
      dwMemSize equ _dwMemSize - $$
As you can see, dwMemSize is initially 0, so BaseAddrLow + LengthLow must be greater than 0.

MemSize = BaseAddrLow + LengthLow reason:
In fact, the memory size is equal to Base Address (baseaddrlow + baseaddrlow) + Length (LengthLow+LengthHigh). However, a BaseAddrLow + LengthLow is 32-bit. It can represent a memory size of 2^32bit, that is, 4GB memory. At that time, the physical memory is not so large, so BaseAddrLow + LengthLow can be used to represent the memory size.
There are also some code for printing memory information in the program. This note only describes the part of getting memory size and calculating the number of page tables.

Finally, attach the complete code (you can see if you are interested, the above line is basically the end of the article, and the code is just for the convenience of people interested in reading the code):

; ==========================================
; pmtest7.asm
; Compilation method: nasm pmtest7.asm -o pmtest7.com
; ==========================================

%include	"pm.inc"	; constant, macro, And some notes

PageDirBase		equ	200000h	; Page directory start address:	2M
PageTblBase		equ	201000h	; Page table start address:		2M + 4K

org	0100h
	jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         Paragraph base address,       Segment limit     , attribute
LABEL_GDT:		Descriptor	       0,                 0, 0			; Null descriptor
LABEL_DESC_NORMAL:	Descriptor	       0,            0ffffh, DA_DRW		; Normal descriptor
LABEL_DESC_PAGE_DIR:	Descriptor   PageDirBase,              4095, DA_DRW		; Page Directory
LABEL_DESC_PAGE_TBL:	Descriptor   PageTblBase,      4096 * 8 - 1, DA_DRW		; Page Tables
LABEL_DESC_CODE32:	Descriptor	       0,  SegCode32Len - 1, DA_C + DA_32	; Inconsistent code snippet, 32
LABEL_DESC_CODE16:	Descriptor	       0,            0ffffh, DA_C		; Inconsistent code snippet, 16
LABEL_DESC_DATA:	Descriptor	       0,	DataLen - 1, DA_DRW		; Data
LABEL_DESC_STACK:	Descriptor	       0,        TopOfStack, DA_DRWA + DA_32	; Stack, 32 position
LABEL_DESC_VIDEO:	Descriptor	 0B8000h,            0ffffh, DA_DRW		; First memory address
; GDT End

GdtLen		equ	$ - LABEL_GDT	; GDT length
GdtPtr		dw	GdtLen - 1	; GDT limit
		dd	0		; GDT Base address

; GDT Selector
SelectorNormal		equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorPageDir		equ	LABEL_DESC_PAGE_DIR	- LABEL_GDT
SelectorPageTbl		equ	LABEL_DESC_PAGE_TBL	- LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]	 ; Data segment
ALIGN	32
[BITS	32]
LABEL_DATA:
; Use these symbols in real mode
; Character string
_szPMMessage:			db	"In Protect Mode now. ^-^", 0Ah, 0Ah, 0	; Display this string after entering protection mode
_szMemChkTitle:			db	"BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0	; Display this string after entering protection mode
_szRAMSize			db	"RAM size:", 0
_szReturn			db	0Ah, 0
; variable
_wSPValueInRealMode		dw	0
_dwMCRNumber:			dd	0	; Memory Check Result
_dwDispPos:			dd	(80 * 6 + 0) * 2	; Screen line 6, The zeroth column.
_dwMemSize:			dd	0
_ARDStruct:			; Address Range Descriptor Structure
	_dwBaseAddrLow:		dd	0
	_dwBaseAddrHigh:	dd	0
	_dwLengthLow:		dd	0
	_dwLengthHigh:		dd	0
	_dwType:		dd	0

_MemChkBuf:	times	256	db	0

; Use these symbols in protected mode
szPMMessage		equ	_szPMMessage	- $$
szMemChkTitle		equ	_szMemChkTitle	- $$
szRAMSize		equ	_szRAMSize	- $$
szReturn		equ	_szReturn	- $$
dwDispPos		equ	_dwDispPos	- $$
dwMemSize		equ	_dwMemSize	- $$
dwMCRNumber		equ	_dwMCRNumber	- $$
ARDStruct		equ	_ARDStruct	- $$
	dwBaseAddrLow	equ	_dwBaseAddrLow	- $$
	dwBaseAddrHigh	equ	_dwBaseAddrHigh	- $$
	dwLengthLow	equ	_dwLengthLow	- $$
	dwLengthHigh	equ	_dwLengthHigh	- $$
	dwType		equ	_dwType		- $$
MemChkBuf		equ	_MemChkBuf	- $$

DataLen			equ	$ - LABEL_DATA
; END of [SECTION .data1]


; Global stack segment
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
	times 512 db 0

TopOfStack	equ	$ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	mov	[LABEL_GO_BACK_TO_REAL+3], ax
	mov	[_wSPValueInRealMode], sp

	; Get the number of memory
	mov	ebx, 0
	mov	di, _MemChkBuf
.loop:
	mov	eax, 0E820h
	mov	ecx, 20
	mov	edx, 0534D4150h
	int	15h
	jc	LABEL_MEM_CHK_FAIL
	add	di, 20
	inc	dword [_dwMCRNumber]
	cmp	ebx, 0
	jne	.loop
	jmp	LABEL_MEM_CHK_OK
LABEL_MEM_CHK_FAIL:
	mov	dword [_dwMCRNumber], 0
LABEL_MEM_CHK_OK:

	; Initialize 16 bit code segment descriptor
	mov	ax, cs
	movzx	eax, ax
	shl	eax, 4
	add	eax, LABEL_SEG_CODE16
	mov	word [LABEL_DESC_CODE16 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE16 + 4], al
	mov	byte [LABEL_DESC_CODE16 + 7], ah

	; Initialize 32-bit segment descriptor
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah

	; Initialize segment descriptor
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_DATA
	mov	word [LABEL_DESC_DATA + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_DATA + 4], al
	mov	byte [LABEL_DESC_DATA + 7], ah

	; Initialize stack segment descriptor
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_STACK
	mov	word [LABEL_DESC_STACK + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_STACK + 4], al
	mov	byte [LABEL_DESC_STACK + 7], ah

	; To load GDTR Prepare for
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt Base address
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt Base address

	; Load GDTR
	lgdt	[GdtPtr]

	; Interrupt interrupt
	cli

	; Open address line A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; Ready to switch to protected mode
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; Real access to protection mode
	jmp	dword SelectorCode32:0	; Implementing this sentence will SelectorCode32 load cs, And jump to Code32Selector:0  place

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:		; From protection mode to real mode, that's it
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax

	mov	sp, [_wSPValueInRealMode]

	in	al, 92h		; ┓
	and	al, One thousand one hundred and eleven 1101b	; ┣ Close A20 Address line
	out	92h, al		; ┛

	sti			; Open interrupt

	mov	ax, 4c00h	; ┓
	int	21h		; ┛Go back to DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 Bit code segment. Jump from real mode.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; Segment selector
	mov	ax, SelectorData
	mov	es, ax
	mov	ax, SelectorVideo
	mov	gs, ax			; Video segment selector

	mov	ax, SelectorStack
	mov	ss, ax			; Stack segment selector

	mov	esp, TopOfStack


	; A string is shown below
	push	szPMMessage
	call	DispStr
	add	esp, 4

	push	szMemChkTitle
	call	DispStr
	add	esp, 4

	call	DispMemSize		; Display memory information

	call	SetupPaging		; Start paging mechanism

	; Stop here
	jmp	SelectorCode16:0

; Start paging mechanism --------------------------------------------------------------
SetupPaging:
	; Calculate how much should be initialized according to the memory size PDE And how many pages
	xor	edx, edx
	mov	eax, [dwMemSize]
	mov	ebx, 400000h	; 400000h = 4M = 4096 * 1024, Memory size of a page table
	div	ebx
	mov	ecx, eax	; here ecx Is the number of page tables, i.e PDE Should be
	test	edx, edx
	jz	.no_remainder
	inc	ecx		; Add a page table if the remainder is not 0
.no_remainder:
	push	ecx		; Number of temporary page tables

	; To simplify processing, All linear addresses correspond to equal physical addresses. And we don't think about memory holes.

	; Initialize page directory first
	mov	ax, SelectorPageDir	; The first address of this segment is PageDirBase
	mov	es, ax
	xor	edi, edi
	xor	eax, eax
	mov	eax, PageTblBase | PG_P  | PG_USU | PG_RWW
.1:
	stosd
	add	eax, 4096		; For simplification, All page tables are contiguous in memory.
	loop	.1

	; Reinitialize all page tables
	mov	ax, SelectorPageTbl	; The first address of this segment is PageTblBase
	mov	es, ax
	pop	eax			; Page count
	mov	ebx, 1024		; 1024 tables per page PTE
	mul	ebx
	mov	ecx, eax		; PTE Number = Page count * 1024
	xor	edi, edi
	xor	eax, eax
	mov	eax, PG_P  | PG_USU | PG_RWW
.2:
	stosd
	add	eax, 4096		; Each page points to 4 K Space
	loop	.2

	mov	eax, PageDirBase
	mov	cr3, eax
	mov	eax, cr0
	or	eax, 80000000h
	mov	cr0, eax
	jmp	short .3
.3:
	nop

	ret
; Paging mechanism started ----------------------------------------------------------



DispMemSize:
	push	esi
	push	edi
	push	ecx

	mov	esi, MemChkBuf
	mov	ecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++)//Get one ARDS at a time
.loop:				  ;{
	mov	edx, 5		  ;  for(int j=0;j<5;j++) //Get members in ARDS one at a time
	mov	edi, ARDStruct	  ;  {//Baseaddrlow, baseaddrlow, lengthlow are displayed in turn,
.1:				  ;             LengthHigh,Type
	push	dword [esi]	  ;
	call	DispInt		  ;    DispInt(MemChkBuf[j*4]); //Show a member
	pop	eax		  ;
	stosd			  ;    ARDStruct[j*4] = MemChkBuf[j*4];
	add	esi, 4		  ;
	dec	edx		  ;
	cmp	edx, 0		  ;
	jnz	.1		  ;  }
	call	DispReturn	  ;  printf("\n");
	cmp	dword [dwType], 1 ;  if(Type == AddressRangeMemory)
	jne	.2		  ;  {
	mov	eax, [dwBaseAddrLow];
	add	eax, [dwLengthLow];
	cmp	eax, [dwMemSize]  ;    if(BaseAddrLow + LengthLow > MemSize)
	jb	.2		  ;
	mov	[dwMemSize], eax  ;    MemSize = BaseAddrLow + LengthLow;
.2:				  ;  }
	loop	.loop		  ;}
				  ;
	call	DispReturn	  ;printf("\n");
	push	szRAMSize	  ;
	call	DispStr		  ;printf("RAM size:");
	add	esp, 4		  ;
				  ;
	push	dword [dwMemSize] ;
	call	DispInt		  ;DispInt(MemSize);
	add	esp, 4		  ;

	pop	ecx
	pop	edi
	pop	esi
	ret

%include	"lib.inc"	; Library function

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 Bit code segment. Jump from 32-bit snippet, Jump out of back to real mode
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:
	; Jump back to real mode:
	mov	ax, SelectorNormal
	mov	ds, ax
	mov	es, ax
	mov	fs, ax
	mov	gs, ax
	mov	ss, ax

	mov	eax, cr0
	and     eax, 7FFFFFFEh          ; PE=0, PG=0
	mov	cr0, eax

LABEL_GO_BACK_TO_REAL:
	jmp	0:LABEL_REAL_ENTRY	; The segment address is set to the correct value at the beginning of the program

Code16Len	equ	$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

There is also a header file% include "lib.inc":

; ------------------------------------------------------------------------
; display AL Number in
; ------------------------------------------------------------------------
DispAL:
	push	ecx
	push	edx
	push	edi

	mov	edi, [dwDispPos]

	mov	ah, 0Fh			; 0000b: Black background    1111b: White character
	mov	dl, al
	shr	al, 4
	mov	ecx, 2
.begin:
	and	al, 0111 1b
	cmp	al, 9
	ja	.1
	add	al, '0'
	jmp	.2
.1:
	sub	al, 0Ah
	add	al, 'A'
.2:
	mov	[gs:edi], ax
	add	edi, 2

	mov	al, dl
	loop	.begin
	;add	edi, 2

	mov	[dwDispPos], edi

	pop	edi
	pop	edx
	pop	ecx

	ret
; DispAL End-------------------------------------------------------------


; ------------------------------------------------------------------------
; Display an integer number
; ------------------------------------------------------------------------
DispInt:
	mov	eax, [esp + 4]
	shr	eax, 24
	call	DispAL

	mov	eax, [esp + 4]
	shr	eax, 16
	call	DispAL

	mov	eax, [esp + 4]
	shr	eax, 8
	call	DispAL

	mov	eax, [esp + 4]
	call	DispAL

	mov	ah, 07h			; 0000b: Black background    0111b: Gray character
	mov	al, 'h'
	push	edi
	mov	edi, [dwDispPos]
	mov	[gs:edi], ax
	add	edi, 4
	mov	[dwDispPos], edi
	pop	edi

	ret
; DispInt End------------------------------------------------------------

; ------------------------------------------------------------------------
; Display a string
; ------------------------------------------------------------------------
DispStr:
	push	ebp
	mov	ebp, esp
	push	ebx
	push	esi
	push	edi

	mov	esi, [ebp + 8]	; pszInfo
	mov	edi, [dwDispPos]
	mov	ah, 0Fh
.1:
	lodsb
	test	al, al
	jz	.2
	cmp	al, 0Ah	; Is it a return??
	jnz	.3
	push	eax
	mov	eax, edi
	mov	bl, 160
	div	bl
	and	eax, 0FFh
	inc	eax
	mov	bl, 160
	mul	bl
	mov	edi, eax
	pop	eax
	jmp	.1
.3:
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1

.2:
	mov	[dwDispPos], edi

	pop	edi
	pop	esi
	pop	ebx
	pop	ebp
	ret
; DispStr End------------------------------------------------------------

; ------------------------------------------------------------------------
; Line feed
; ------------------------------------------------------------------------
DispReturn:
	push	szReturn
	call	DispStr			;printf("\n");
	add	esp, 4

	ret
; DispReturn End---------------------------------------------------------


; ------------------------------------------------------------------------
; Memory copy memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
	push	ebp
	mov	ebp, esp

	push	esi
	push	edi
	push	ecx

	mov	edi, [ebp + 8]	; Destination
	mov	esi, [ebp + 12]	; Source
	mov	ecx, [ebp + 16]	; Counter
.1:
	cmp	ecx, 0		; Judgment counter
	jz	.2		; Jump out when counter is zero

	mov	al, [ds:esi]		; ┓
	inc	esi			; ┃
					; ┣ Move byte by byte
	mov	byte [es:edi], al	; ┃
	inc	edi			; ┛

	dec	ecx		; Counter minus one
	jmp	.1		; loop
.2:
	mov	eax, [ebp + 8]	; Return value

	pop	ecx
	pop	edi
	pop	esi
	mov	esp, ebp
	pop	ebp

	ret			; Function end, return
; MemCpy End-------------------------------------------------------------


Published 26 original articles, won praise 2, visited 1894
Private letter follow

Tags: Attribute

Posted on Mon, 03 Feb 2020 23:24:53 -0800 by Richzilla