간단?한 hello world 출력 예제인데.. hello world 출력하는 코드가 아래랑 같다.
.text
_start: .global _start
@ sys_write ( fd, pstr, len )
@ r7=4 r0 r1 r2
mov r0, #1 @fd <- stdout
adr r1, msg @pstr <- *msg
mov r2, #14 @len <- 14
mov r7, #4 @syscall <-sys_write
swi 0 @system call occur interupt
@sys_exit (exitcode)
@r7=1 r0
mov r0, #0 @ exitcode <- 0
mov r7, #1 @ syscall <- sys_exit
swi 0 @ system call
msg:
.asciz "Hello, world!\n"
.end
천천히 코드를 하나하나 뜯어보자, 일단 큼지막하게 .text, _start, .global, msg:, .end로 나눠보자.
섹션
크게 데이터 섹션과 코드 섹션으로 나눌 수 있고, 각각 .data, .text로 나뉜다.
데이터 섹션에는 프로그램에서 사용하는 데이터 (변수, 상수, 배열, 문자열 등)이 포함되고, .data 혹은 .section .data와 같은 지시문으로 정의된다. 이 섹션에 선언된 데이터는 프로그램 실행 중 메모리에 로드되고 수정될 수 있다.
코드 섹션은 프로그램의 명령어와 함수 정의를 저장하는 공간이다. 코드 섹션은 .text 또는 .section .text와 같은 지시문으로 정의되고, 프로그램의 메인 루틴과 하위 루틴이 포함된다. 코드 섹션에 있는 명령어들은 프로세서가 실행할 명령어로 변환되어 메모리에 로드된다.
레이블
레이블은 어셈블리어에서 특정한 위치나 명령어를 참조하기 위해 사용하는 식별자이다. 주로 프로그램의 특정 위치나 분기점에서 목적지를 지정하는데 쓰이고, 해당 코드의 주소에 대한 참조를 뜻한다.
[레이블명]: 과 같은 형식을 갖고, 뒤에는 콜론을 붙여준다.
.text
.text는 어셈블리 코드에서 실제 명령어가 저장되는 곳이며, 프로그램의 시작점과 실제 작업을 수행하는 명령어들이 들어간다. 프로그램이 실행되면 CPU가 .text 섹션에 있는 명령어를 하나씩 읽어서 실행한다.
_start
_start는 프로그램 시작 지점을 나타내는 표식으로, 컴퓨터는 _start 레이블이 표시된 곳으로부터 명령어를 실행해나간다. 이곳으로부터 프로그램이 시작되고, 필요한 초기화를 수행한다.
.global
.global은 어셈블리 코드에서 레이블을 외부에서 사용할 수 있도록 정의하는데 사용된다. global을 사용하면 다른 파일이나 모듈에서 해당 레이블을 참조할 수 있게 된다.
msg:
문자열을 저장하는데 쓴 데이터 섹션의 레이블이다. "Hello, world!\n"이라는 문자열을 저장하고 있으며, 안에서 .asciz지시자를 사용하여 문자열의 끝에 널 문자를 자동으로 추가한다. msg레이블은 문자열의 시작 주소를 의미하고, 이 주소를 활용하여 출력하거나 다른 연산을 수행할 수 있다.
.end
.end 지시문은 어셈블리 코드의 끝을 표시하는 지시문이다.
이제 레지스터에 대해 알아보자,
레지스터
레지스터는 CPU에 존재하는 데이터 저장 공간이다. 매우 빠른 속도로 접근이 가능하며, 연산 수행 및 저장을 담당한다.
위 어셈블리 코드에서 rN은 레지스터를 뜻한다. r0,r1,r2는 함수 인수 레지스터이며, r0은 시스템 호출에서 파일 설명자 혹은 반환 값을 전달하는데 사용한다.
레지스터 | r0 | #1 | #2 | #3 | #4 |
r0 | stdin / exit | stdout | |||
r1 | 내용 pointer | ||||
r2 | 내용 길이 | ||||
r7 | sys_exit | sys_read | sys_write |
연산
연산 | 형태 | 의미 |
mov | mov rN V | V값을 rN에 저장 |
ldr | ldr rN [name] | name의 메모리 주소에 저장되어 있는 값을 rN에 저장, 전체부분에서 사용가능 |
adr | adr rN [name] | name의 메모리 주소를 rN에 저장, 코드섹션에서만 사용 |
str | str rN rM | rN의 데이터를 rM에 저장한다 |
swi | swi 0 | 인터럽트를 발생시킨다. 현재 레지스터에 들어있는 값에 따라 명령을 수행한다 정도로 해석해도 될듯 싶다 |
add | add rN,rA,rB | rN에 rA+rB를 저장한다, rB를 생략하면 rN=rN+rA이다. |
sub | sub rN,rA,rB | rN에 rA-rB를 저장한다, rB를 생략하면 rN=rN-rA이다. |
rsb | rsb rN, rA,rB | rN에 rB-rA를 저장한다. rB를 생략하면 rN=0-rA |
cmp | cmp rN, V | rN과 V를 비교해서, 두 개의 값 비교 결과를 프로세서 상태 레지스터에 저장한다. 그 결과는 다음과 같다 ZF : Zero Flag, rN == V 같은 경우 1이 된다. NF : Negative Flag, rN<V 인 경우 1 CF : Carry Flag, rN>V 인 경우 1 |
b | b [분기] | 브렌치, 분기점으로 분기한다. lr의 경우 이전 코드의 시작점으로 간다 |
'프로그래밍 > 개인공부저장용' 카테고리의 다른 글
[TOPCIT Essence] 02 소프트웨어 개발 방법론 (0) | 2024.05.03 |
---|---|
[TOPCIT Essence] 01 소프트웨어 공학의 배경과 목적 (2) | 2024.05.03 |
KMP 완전히 이해해보기 - 1 (1) | 2022.09.21 |