同一Cソースから生成されたアセンブリの比較：MS-DOS版Turbo CとWindows版GCCの違い

## Ｃ言語ソース
```demo.c
#include <stdio.h>

int add(int c, int d) {
    return c + d;
}

int main() {
    int a = 10;
    int b = 20;
	int result = add(10, 20);
    
    if (a > b) {
        printf("a is greater\n");
    } else {
        printf("b is greater\n");
    }
    
    return 0;
}
```

## 生成されたアセンブリ
以下のコマンドを使って、それぞれの環境でアセンブリを生成しました：
```bash
tcc -S -IC:\TC\INCLUDE yourfile.c #msdos tarbo c
gcc -S -masm=intel demo.c -o demo.asm #win10 gcc
```
<details>
<summary>msdos</summary>

```demo.asm
	ifndef	??version
?debug	macro
	endm
	endif
	?debug	S "demo.c"
_TEXT	segment	byte public 'CODE'
DGROUP	group	_DATA,_BSS
	assume	cs:_TEXT,ds:DGROUP,ss:DGROUP
_TEXT	ends
_DATA	segment word public 'DATA'
d@	label	byte
d@w	label	word
_DATA	ends
_BSS	segment word public 'BSS'
b@	label	byte
b@w	label	word
	?debug	C E9206AAA5A0664656D6F2E63
	?debug	C E9807E493115433A5C74635C696E636C7564655C737464696F2E68
	?debug	C E9807E493116433A5C74635C696E636C7564655C7374646172672E+
	?debug	C 68
_BSS	ends
_TEXT	segment	byte public 'CODE'
;	?debug	L 3
_add	proc	near
	push	bp
	mov	bp,sp
;	?debug	L 4
	mov	ax,word ptr [bp+4]
	add	ax,word ptr [bp+6]
	jmp	short @1
@1:
;	?debug	L 5
	pop	bp
	ret	
_add	endp
;	?debug	L 7
_main	proc	near
	push	bp
	mov	bp,sp
	sub	sp,2
	push	si
	push	di
;	?debug	L 8
	mov	si,10
;	?debug	L 9
	mov	di,20
;	?debug	L 10
	mov	ax,20
	push	ax
	mov	ax,10
	push	ax
	call	near ptr _add
	pop	cx
	pop	cx
	mov	word ptr [bp-2],ax
;	?debug	L 12
	cmp	si,di
	jle	@3
;	?debug	L 13
	mov	ax,offset DGROUP:s@
	push	ax
	call	near ptr _printf
	pop	cx
;	?debug	L 14
	jmp	short @4
@3:
;	?debug	L 15
	mov	ax,offset DGROUP:s@+14
	push	ax
	call	near ptr _printf
	pop	cx
@4:
;	?debug	L 18
	xor	ax,ax
	jmp	short @2
@2:
;	?debug	L 19
	pop	di
	pop	si
	mov	sp,bp
	pop	bp
	ret	
_main	endp
_TEXT	ends
	?debug	C E9
_DATA	segment word public 'DATA'
s@	label	byte
	db	97
	db	32
	db	105
	db	115
	db	32
	db	103
	db	114
	db	101
	db	97
	db	116
	db	101
	db	114
	db	10
	db	0
	db	98
	db	32
	db	105
	db	115
	db	32
	db	103
	db	114
	db	101
	db	97
	db	116
	db	101
	db	114
	db	10
	db	0
_DATA	ends
_TEXT	segment	byte public 'CODE'
	extrn	_printf:near
_TEXT	ends
	public	_main
	public	_add
	end

```
</details>

<details>
<summary>win10</summary>

```demo.asm
	.file	"demo.c"
	.intel_syntax noprefix
	.text
	.globl	add
	.def	add;	.scl	2;	.type	32;	.endef
	.seh_proc	add
add:
	push	rbp
	.seh_pushreg	rbp
	mov	rbp, rsp
	.seh_setframe	rbp, 0
	.seh_endprologue
	mov	DWORD PTR 16[rbp], ecx
	mov	DWORD PTR 24[rbp], edx
	mov	edx, DWORD PTR 16[rbp]
	mov	eax, DWORD PTR 24[rbp]
	add	eax, edx
	pop	rbp
	ret
	.seh_endproc
	.section .rdata,"dr"
.LC0:
	.ascii "a is greater\0"
.LC1:
	.ascii "b is greater\0"
	.text
	.globl	main
	.def	main;	.scl	2;	.type	32;	.endef
	.seh_proc	main
main:
	push	rbp
	.seh_pushreg	rbp
	mov	rbp, rsp
	.seh_setframe	rbp, 0
	sub	rsp, 48
	.seh_stackalloc	48
	.seh_endprologue
	call	__main
	mov	DWORD PTR -4[rbp], 10
	mov	DWORD PTR -8[rbp], 20
	mov	edx, 20
	mov	ecx, 10
	call	add
	mov	DWORD PTR -12[rbp], eax
	mov	eax, DWORD PTR -4[rbp]
	cmp	eax, DWORD PTR -8[rbp]
	jle	.L4
	lea	rax, .LC0[rip]
	mov	rcx, rax
	call	puts
	jmp	.L5
.L4:
	lea	rax, .LC1[rip]
	mov	rcx, rax
	call	puts
.L5:
	mov	eax, 0
	add	rsp, 48
	pop	rbp
	ret
	.seh_endproc
	.def	__main;	.scl	2;	.type	32;	.endef
	.ident	"GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0"
	.def	puts;	.scl	2;	.type	32;	.endef

```
</details>

## 差異

### 引数の渡し方
`add(10, 20)`の呼び出し部分における引数の渡し方は、MS-DOS環境とWindows環境で異なります。

MS-DOS (Turbo C) では、引数は スタック に積まれまれています
```msdos.s
	mov	ax,20
	push	ax
	mov	ax,10
	push	ax
	call	near ptr _add
```
Windows (GCC) では、引数は レジスタ（ecx, edx） を使って渡されています
```win10.s
	mov	edx, 20
	mov	ecx, 10
	call	add
```

### printfの呼び出し
win10ではputsを呼び出しています
一方、Turbo Cでは明示的に_printfが呼び出されています：
(文字列に書式指定（%dなど）が含まれていないため、GCCが最適化してputsに置き換えた？？)
```msdos.s
	mov	ax,offset DGROUP:s@
	push	ax
	call	near ptr _printf
```
```win10.s
	lea	rax, .LC0[rip]
	mov	rcx, rax
	call	puts
```

### intの大きさ
`return c + d`の部分を比較すると、それぞれの環境でint型の扱いが異なることが分かります。
MS-DOS (16bit) では、ax（16ビットレジスタ）を使用している
```msdos.s
	mov	ax,word ptr [bp+4]
	add	ax,word ptr [bp+6]
```
Windows10では、eax（32ビットレジスタ）を使用している
```win10.s
	mov	DWORD PTR 16[rbp], ecx
	mov	DWORD PTR 24[rbp], edx
	mov	edx, DWORD PTR 16[rbp]
	mov	eax, DWORD PTR 24[rbp]
	add	eax, edx
```
これにより、MS-DOS環境ではintが2バイト、Windows環境では4バイトとして扱われていることが確認できます。

## 動作環境
MS DOS　6.22
Turbo C 2.0

Windows10 (10.0.19045.5737)
gcc version 14.2.0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/c44afd08-4183-4e92-92b2-00ffe7463338.png)