메뉴 English Ukrainian 러시아인 홈

애호가 및 전문가를 위한 무료 기술 라이브러리 무료 기술 라이브러리


강의 요약, 유아용 침대
무료 도서관 / 핸드북 / 강의 노트, 치트 시트

정보학 및 정보 기술. 강의 노트: 간략하게, 가장 중요한

강의 노트, 치트 시트

핸드북 / 강의 노트, 치트 시트

기사에 대한 의견 기사에 대한 의견

차례

  1. 컴퓨터 과학 개론 (정보학. 정보. 정보의 표현 및 처리. 숫자 체계. 컴퓨터에서의 숫자 표현. 알고리즘의 형식화된 개념)
  2. 파스칼 언어 (파스칼 소개. 표준 프로시저 및 함수. 파스칼 연산자)
  3. 절차 및 기능 (보조 알고리즘의 개념. 파스칼의 절차. 파스칼의 함수. 예상 설명 및 서브루틴 연결. 지시어)
  4. 서브루틴 (루틴 매개변수. 서브루틴 매개변수 유형. 파스칼의 문자열 유형. 문자열 유형 변수에 대한 프로시저 및 함수. 레코드. 세트)
  5. 파일 (파일. 파일 작업. 모듈. 모듈 유형)
  6. 동적 메모리 (참조 데이터 유형. 동적 메모리. 동적 변수. 동적 메모리 작업. 유형이 지정되지 않은 포인터)
  7. 추상 데이터 구조 (추상 데이터 구조. 스택. 큐)
  8. 트리 데이터 구조 (트리 데이터 구조. 트리 작업. 작업 구현 예)
  9. 카운트 (그래프의 개념. 그래프를 표현하는 방법. 발생률 목록으로 그래프를 표현. 그래프의 깊이 우선 순회 알고리즘. 그래프를 목록의 목록으로 표현. 그래프의 너비 우선 순회 알고리즘 )
  10. 객체 데이터 유형 (파스칼의 객체 유형. 객체의 개념, 설명 및 사용. 상속. 객체의 인스턴스 생성. 구성 요소 및 범위)
  11. 방법 (메서드. 생성자 및 소멸자. 소멸자. 가상 메서드. 개체 데이터 필드 및 형식 메서드 매개 변수)
  12. 개체 유형 호환성 (캡슐화. 확장 가능한 객체. 객체 유형 호환성)
  13. 어셈블러 (어셈블러 정보. 마이크로프로세서 소프트웨어 모델. 사용자 레지스터. 범용 레지스터. 세그먼트 레지스터. 상태 및 제어 레지스터)
  14. 레지스터 (마이크로프로세서 시스템 레지스터. 제어 레지스터. 시스템 주소 레지스터. 디버그 레지스터)
  15. 조립 프로그램 (어셈블러 프로그램 구조. 어셈블러 구문. 비교 연산자. 연산자 및 우선 순위. 단순화된 세그먼트 정의 지시문. MODEL 지시문에 의해 생성된 식별자. 메모리 모델. 메모리 모델 수정자)
  16. 조립 지침 구조 (기계 명령어의 구조. 명령어 피연산자를 지정하는 방법. 주소 지정 방법)
  17. (데이터 전송 명령. 산술 명령)
  18. 제어 전송 명령 (논리 명령. 논리적 부정에 대한 진리표. 논리적 포함 OR에 대한 진리표. 논리적 AND에 대한 진리표. 논리적 배타적 OR에 대한 진리표. jcc 명령 이름의 약어 의미. 명령에 대한 조건부 점프 명령 목록. 조건부 점프 명령 및 플래그)

LECTURE No. 1. 컴퓨터 공학 입문

1. 컴퓨터 과학. 정보. 정보의 표시 및 처리

정보학은 과학, 기술 및 생산의 다양한 분야에서 개체 및 관계 구조의 형식화된 표현에 종사하고 있습니다. 다양한 형식 도구는 논리 공식, 데이터 구조, 프로그래밍 언어 등과 같은 객체 및 현상을 모델링하는 데 사용됩니다.

컴퓨터 과학에서 정보와 같은 기본 개념은 다양한 의미를 갖습니다.

1) 외부 형태의 정보에 대한 공식적인 표현

2) 정보의 추상적 의미, 내부 내용, 의미론;

3) 현실 세계에 대한 정보의 관계.

그러나 일반적으로 정보는 추상적 의미인 의미론으로 이해됩니다. 정보의 표현을 해석함으로써 우리는 그 의미, 의미론을 얻습니다. 따라서 정보를 교환하려면 해석의 정확성이 침해되지 않도록 일관된 관점이 필요합니다. 이를 위해 정보 표현의 해석은 일부 수학적 구조로 식별됩니다. 이 경우 엄격한 수학적 방법으로 정보 처리를 수행할 수 있습니다.

정보에 대한 수학적 설명 중 하나는 함수 y =f(x, t) 형식으로 표현하는 것입니다. 여기서 t는 시간이고 x는 y 값이 측정되는 특정 필드의 지점입니다. 카이함수(정보)의 매개변수에 따라 분류될 수 있다.

매개변수가 연속적인 값을 취하는 스칼라 양인 경우 이러한 방식으로 얻은 정보를 연속(또는 아날로그)이라고 합니다. 매개변수에 특정 변경 단계가 주어지면 정보를 이산이라고 합니다. 개별 정보는 각 특정 매개변수에 대해 주어진 정확도로 함수 값을 얻을 수 있기 때문에 보편적인 것으로 간주됩니다.

이산 정보는 일반적으로 디지털 정보로 식별되며, 이는 알파벳 표현의 기호 정보의 특수한 경우입니다. 알파벳은 모든 성격의 유한한 기호 집합입니다. 컴퓨터 과학에서는 인코딩 작업을 수행하기 위해 한 알파벳의 문자가 다른 알파벳의 문자로 표현되어야 하는 상황이 매우 자주 발생합니다. 인코딩 알파벳의 문자 수가 인코딩 알파벳의 문자 수보다 적으면 인코딩 작업 자체가 복잡하지 않습니다. 그렇지 않으면 모호하지 않은 올바른 인코딩을 위해 인코딩 알파벳의 고정 문자 집합을 사용해야 합니다.

실습에서 알 수 있듯이 다른 알파벳을 인코딩할 수 있는 가장 간단한 알파벳은 일반적으로 0과 1로 표시되는 두 개의 문자로 구성된 바이너리입니다. 바이너리 알파벳의 n자를 사용하면 2n개의 문자를 인코딩할 수 있으며 이 정도면 충분합니다. 모든 알파벳을 인코딩합니다.

이진 알파벳의 기호로 나타낼 수 있는 값을 정보 또는 비트의 최소 단위라고 합니다. 8비트 시퀀스 - 바이트. 256개의 서로 다른 8비트 시퀀스를 포함하는 알파벳을 바이트 알파벳이라고 합니다.

오늘날 컴퓨터 과학의 표준으로 각 문자를 1바이트로 인코딩하는 코드가 채택됩니다. 다른 알파벳도 있습니다.

2. 번호 체계

숫자 체계는 숫자를 명명하고 쓰기 위한 일련의 규칙입니다. 위치 및 비 위치 번호 시스템이 있습니다.

숫자의 숫자 값이 숫자의 숫자 위치에 따라 달라지는 경우 숫자 체계를 위치 지정이라고 합니다. 그렇지 않으면 비포지셔닝이라고 합니다. 숫자의 값은 숫자에서 이러한 숫자의 위치에 따라 결정됩니다.

3. 컴퓨터에서의 숫자 표현

32비트 프로세서는 최대 232-1개의 RAM에서 작동할 수 있으며 주소는 00000000 - FFFFFFFF 범위에서 쓸 수 있습니다. 그러나 리얼 모드에서 프로세서는 최대 220-1의 메모리로 작동하며 주소는 00000 - FFFFF 범위에 속합니다. 메모리 바이트는 고정 및 가변 길이의 필드로 결합될 수 있습니다. 워드는 2바이트로 구성된 고정 길이 필드이고, 더블 워드는 4바이트 필드입니다. 필드 주소는 짝수 또는 홀수일 수 있으며 짝수 주소가 더 빠릅니다.

고정 소수점 숫자는 컴퓨터에서 정수 이진수로 표시되며 크기는 1, 2 또는 4바이트일 수 있습니다.

이진 정수는 2의 보수로 표현되고, 고정 소수점 숫자는 10의 보수로 표현됩니다. 또한 숫자가 XNUMX바이트를 차지하면 숫자 구조는 다음 규칙에 따라 작성됩니다. 가장 중요한 숫자는 숫자의 부호에 할당되고 나머지는 숫자의 이진 숫자에 할당됩니다. 양수의 보수 코드는 숫자 자체와 동일하며, 음수의 보수 코드는 다음 공식을 사용하여 얻을 수 있습니다: x = XNUMXi - \x\, 여기서 n은 숫자의 자릿수 용량입니다.

이진수 시스템에서 비트를 반전하여 추가 코드를 얻습니다. 즉, 단위를 XNUMX으로 또는 그 반대로 바꾸고 최하위 비트에 XNUMX을 더합니다.

가수의 비트 수는 숫자 표현의 정밀도를 결정하고 기계 순서 비트 수는 부동 소수점 숫자의 표현 범위를 결정합니다.

4. 알고리즘의 정형화된 개념

알고리즘은 동시에 어떤 수학적 대상이 존재하는 경우에만 존재할 수 있습니다. 알고리즘의 형식화된 개념은 재귀 함수, 일반 마르코프 알고리즘, 튜링 기계의 개념과 연결됩니다.

수학에서 어떤 인수 집합에 대해 함수의 고유한 값이 결정되는 법칙이 있는 경우 함수를 단일 값이라고 합니다. 알고리즘은 그러한 법칙처럼 작용할 수 있습니다. 이 경우 함수는 계산 가능하다고 합니다.

재귀 함수는 계산 가능한 함수의 하위 클래스이며 계산을 정의하는 알고리즘을 컴패니언 재귀 함수 알고리즘이라고 합니다. 첫째, 기본 재귀 함수가 고정되어 수반되는 알고리즘이 사소하고 모호하지 않습니다. 그런 다음 세 가지 규칙(대체, 재귀 및 최소화 연산자)이 도입되며, 이를 통해 기본 기능을 기반으로 더 복잡한 재귀 기능을 얻을 수 있습니다.

기본 기능과 그에 수반되는 알고리즘은 다음과 같습니다.

1) XNUMX과 동일하게 n 독립 변수의 함수. 그런 다음 함수의 부호가 φn이면 인수의 수에 관계없이 함수 값은 XNUMX으로 설정되어야 합니다.

2) ψni 형식의 n 독립 변수의 항등 함수. 그런 다음 함수의 부호가 ψni이면 함수의 값은 왼쪽에서 오른쪽으로 세면서 i 번째 인수의 값으로 취해야 합니다.

3) Λ는 하나의 독립적인 인수의 함수입니다. 그런 다음 함수의 부호가 λ이면 함수의 값은 인수 값 다음에 오는 값으로 취해야 합니다. 다른 학자들은 형식화 된 것에 대한 자신의 접근 방식을 제안했습니다.

알고리즘의 표현. 예를 들어, 미국 과학자 Church는 계산 가능한 함수 클래스가 재귀 함수에 의해 소진되고 결과적으로 음이 아닌 정수 집합을 다른 집합으로 처리하는 알고리즘이 무엇이든 간에 재귀 함수에 수반되는 알고리즘이 있다고 제안했습니다. 주어진 것과 동일합니다. 따라서 주어진 문제를 해결하기 위해 재귀 함수를 구성하는 것이 불가능하면 이를 해결하기 위한 알고리즘이 없습니다. 또 다른 과학자인 Turing은 입력된 문자 시퀀스를 출력으로 처리하는 가상 컴퓨터를 개발했습니다. 이와 관련하여 그는 모든 계산 가능한 함수는 튜링 계산 가능하다는 테제를 제시했습니다.

강의 2. 파스칼 언어

1. 파스칼 언어 소개

언어의 기본 기호(문자, 숫자 및 특수 문자)는 알파벳을 구성합니다. 파스칼 언어에는 다음과 같은 기본 기호 집합이 포함됩니다.

1) 26개의 라틴 소문자 및 26개의 라틴 대문자:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz;

2) _(밑줄);

3) 10자리: 0123456789;

4) 작동 징후:

+ - x / = <> < > <= >= := @;

5) 리미터:

., ' ( ) [ ] (..) { } (* *).. : ;

6) 지정자: ^ # $;

7) 서비스(예약) 단어:

절대, 어셈블러, AND, ARRAY, ASM, 시작, CASE, CONST, 생성자, 소멸자, DIV, DO, DOWNTO, ELSE, END, 내보내기, 외부, FAR, FILE, FOR, FORWARD, FUNCTION, GOTO, IF, implementation, IN, INDEX, Inherited, INLINE, INTERFACE, INTERRUPT, LABEL, LIBRARY, MOD, NAME, NIL, NEAR, NOT, OBJECT, OF, OR, PACKED, PRIVATE, PROCEDURE, PROGRAM, PUBLIC, RECORD, REPEAT, RESIDENT, SET, SHL, SHR, STRING, THEN, TO, TYPE, UNIT, UNTIL, USES, VAR, VIRTUAL, WHILE, WITH, XOR.

나열된 것 외에도 기본 문자 세트에는 공백이 포함됩니다. 이중 문자 및 예약어에는 공백을 사용할 수 없습니다.

데이터의 유형 개념

수학에서는 몇 가지 중요한 특성에 따라 변수를 분류하는 것이 일반적입니다. 실제, 복합 및 논리 변수, 개별 값을 나타내는 변수와 값 집합 등을 엄격하게 구분합니다. 컴퓨터에서 데이터를 처리할 때 이러한 분류는 훨씬 더 중요합니다. 모든 알고리즘 언어에서 모든 상수, 변수, 표현식 또는 함수는 특정 유형입니다.

파스칼에는 규칙이 있습니다. 유형은 사용 전에 변수나 함수의 선언에 명시적으로 지정됩니다. Pascal 유형 개념에는 다음과 같은 주요 속성이 있습니다.

1) 모든 데이터 유형은 변수 또는 표현식이 취할 수 있거나 연산이나 함수가 생성할 수 있는 상수가 속하는 값 세트를 정의합니다.

2) 상수, 변수 또는 표현식이 제공하는 값의 유형은 형식이나 설명에 따라 결정될 수 있습니다.

3) 각 연산이나 함수에는 고정 유형 인수가 필요하며 고정 유형 결과를 생성합니다.

따라서 컴파일러는 유형 정보를 사용하여 다양한 구성의 계산 가능성과 정확성을 확인할 수 있습니다.

유형은 다음을 정의합니다.

1) 주어진 유형에 속하는 변수, 상수, 함수, 표현식의 가능한 값;

2) 컴퓨터에서 데이터 표시의 내부 형식;

3) 주어진 유형에 속하는 값에 대해 수행할 수 있는 작업 및 기능.

유형에 대한 필수 설명은 프로그램 텍스트의 중복으로 이어지지만 이러한 중복은 프로그램 개발을 위한 중요한 보조 도구이며 현대 고급 알고리즘 언어의 필수 속성으로 간주됩니다.

Pascal에는 스칼라 및 구조화된 데이터 유형이 있습니다. 스칼라 유형에는 표준 유형과 사용자 정의 유형이 있습니다. 표준 유형에는 정수, 실수, 문자, 부울 및 주소 유형이 포함됩니다.

정수 유형은 주어진 컴퓨터에서 허용되는 정수 집합에 의해 값이 실현되는 상수, 변수 및 함수를 정의합니다.

실수 유형은 주어진 컴퓨터에서 허용되는 실수의 하위 집합으로 구현되는 데이터를 정의합니다.

사용자 정의 유형은 enum 및 range입니다. 구조화된 유형은 배열, 집합, 레코드 및 파일의 네 가지 유형으로 제공됩니다.

나열된 것 외에도 Pascal에는 두 가지 유형(절차 및 객체)이 더 있습니다.

언어 표현식은 상수, 변수, 함수 포인터, 연산자 기호 및 대괄호로 구성됩니다. 표현식은 일부 값을 계산하기 위한 규칙을 정의합니다. 계산 순서는 계산에 포함된 작업의 우선 순위(우선 순위)에 따라 결정됩니다. 파스칼에는 다음과 같은 연산자 우선 순위가 있습니다.

1) 괄호 안의 계산

2) 기능 값의 계산;

3) 단항 연산;

4) 연산 *, /, div, mod, 및;

5) 연산 +, -, 또는, xor;

6) 관계 연산 =, <>, <, >, <=, >=.

표현식은 많은 파스칼 언어 연산자의 일부이며 내장 함수에 대한 인수가 될 수도 있습니다.

2. 표준 절차 및 기능

산술 함수

1. 기능 Abs(X);

매개변수의 절대값을 반환합니다.

X는 실수 또는 정수 유형의 표현식입니다.

2. 기능 ArcTan(X: 확장): 확장;

인수의 아크 탄젠트를 반환합니다.

X는 실수 또는 정수 유형의 표현식입니다.

3. 함수 exp(X: 실수): 실수;

지수를 반환합니다.

X는 실수 또는 정수 유형의 표현식입니다.

4.Frac(X:실제):실제;

인수의 소수 부분을 반환합니다.

X는 실수형 표현식입니다. 결과는 X의 소수 부분입니다.

Frac(X) = X-Int(X).

5. 함수 Int(X: 실수): 실수;

인수의 정수 부분을 반환합니다.

X는 실수형 표현식입니다. 결과는 X의 정수 부분입니다. 즉, X는 XNUMX으로 반올림됩니다.

6. 함수 Ln(X: 실수): 실수;

실수형 표현식 X의 자연 로그(Ln e = 1)를 반환합니다.

7. 기능 파이: 확장;

3.1415926535로 정의된 Pi 값을 반환합니다.

8. 함수 죄(X: 확장): 확장;

인수의 사인을 반환합니다.

X는 실수형 표현식입니다. Sin은 각도 X의 사인을 라디안으로 반환합니다.

9.Function Sqr(X: 확장): 확장;

인수의 제곱을 반환합니다.

X는 부동 소수점 표현식입니다. 결과는 X와 같은 유형입니다.

10.Function Sqrt(X: 확장): 확장;

인수의 제곱근을 반환합니다.

X는 부동 소수점 표현식입니다. 결과는 X의 제곱근입니다.

가치 변환 절차 및 기능

1. 프로시저 Str(X [: 너비 [: 소수점]]; var S);

에 따라 숫자 X를 문자열 표현으로 변환합니다.

너비 및 소수점 형식 옵션. X는 실수 또는 정수 유형의 표현식입니다. Width 및 Decimals는 정수 유형 표현식입니다. 확장된 구문이 허용되는 경우 S는 String 유형의 변수이거나 null로 끝나는 문자 배열입니다.

2. 기능 Chr(X: 바이트): Char;

ASCII 테이블에서 서수가 X인 문자를 반환합니다.

3. 기능 높음(X);

매개변수 범위에서 가장 큰 값을 반환합니다.

4.FunctionLow(X);

매개변수 범위에서 가장 작은 값을 반환합니다.

5 FunctionOrd(X): Longint;

열거형 식의 서수 값을 반환합니다. X는 열거형 식입니다.

6. 기능 라운드(X: 확장): Longint;

실수 값을 정수로 반올림합니다. X는 실수형 표현식입니다. Round는 가장 가까운 정수로 반올림된 X 값인 Longint 값을 반환합니다. X가 두 정수의 정확히 중간이면 절대값이 가장 큰 숫자가 반환됩니다. X의 반올림된 값이 Longint 범위를 벗어나면 EInvalidOp 예외를 사용하여 처리할 수 있는 런타임 오류가 생성됩니다.

7. 기능 Trunc(X: 확장): Longint;

실수 유형 값을 정수로 자릅니다. X의 반올림된 값이 Longint 범위를 벗어나면 EInvalidOp 예외를 사용하여 처리할 수 있는 런타임 오류가 생성됩니다.

8. 프로시저 Val(S, var V, var 코드: 정수),

숫자를 문자열 값 S에서 숫자로 변환합니다.

표현 V. S - 문자열 유형 표현 - 정수 또는 실수를 형성하는 일련의 문자. S 표현식이 유효하지 않으면 유효하지 않은 문자의 인덱스가 Code 변수에 저장됩니다. 그렇지 않으면 코드가 XNUMX으로 설정됩니다.

서수 값 절차 및 함수

1. 프로시저 Dec(varX [; N: LongInt]);

변수 X에서 하나 또는 N을 뺍니다. Dec(X)는 X:= X - 1에 해당하고 Dec(X, N)은 X:= X - N에 해당합니다. X는 열거형 또는 확장된 경우 PChar 유형의 변수입니다. 구문이 허용되며 N은 정수 유형의 표현식입니다. Dec 프로시저는 최적의 코드를 생성하며 긴 루프에서 특히 유용합니다.

2. 프로시저 Inc(varX [; N: LongInt]);

변수 X에 하나 또는 N을 추가합니다. X는 확장 구문이 허용되는 경우 열거형 또는 PChar 유형의 변수이고 N은 정수형의 식입니다. Inc(X)는 명령 X:= X + 1과 일치하고 Inc(X, N)는 명령 X:= X + N과 일치합니다. Inc 프로시저는 최적의 코드를 생성하며 긴 루프에서 특히 유용합니다.

3. FunctionOdd(X: LongInt): 부울;

X가 홀수이면 True, 그렇지 않으면 False를 반환합니다.

4.FunctionPred(X);

매개변수의 이전 값을 반환합니다. X는 열거형 식입니다. 결과는 같은 유형입니다.

5 함수 Succ(X);

다음 매개변수 값을 반환합니다. X는 열거형 식입니다. 결과는 같은 유형입니다.

3. 파스칼 언어 연산자

조건 연산자

완전한 조건문의 형식은 다음과 같이 정의됩니다. If B then SI else S2; 여기서 B는 분기 조건(의사결정), 논리식 또는 관계입니다. SI, S2 - 하나의 실행 가능한 명령문, 단순 또는 복합.

조건문을 실행할 때 먼저 표현식 B가 평가된 다음 그 결과가 분석됩니다. B가 true이면 명령문 S1이 실행됩니다. then의 분기, 명령문 S2는 건너뜁니다. B가 거짓이면 명령문 S2 - else 분기가 실행되고 명령문 S1을 건너뜁니다.

조건부 연산자의 축약형도 있습니다. 다음과 같이 작성됩니다. B이면 S입니다.

문 선택

연산자 구조는 다음과 같습니다.

케이스 S

c1: 지시1;

c2: 지시2;

...

cn: 명령N;

다른 지시

끝;

여기서 S는 값이 계산되는 서수 유형 표현식입니다.

с1, с2..., сп - 표현식이 비교되는 순서 유형의 상수

에스; Instruction1,..., InstructionN - 상수가 표현식 S의 값과 일치하는 연산자가 실행되는 연산자입니다.

명령 - Sylq 표현식의 값이 상수 c1, c2....cn과 일치하지 않는 경우 실행되는 명령문입니다.

이 연산자는 임의의 수의 대안에 대한 조건부 If 연산자의 일반화입니다. else 분기가 없는 문장의 축약형이 있습니다.

매개변수가 있는 루프 문

for라는 단어로 시작하는 매개변수 루프 문은 복합 명령문이 될 수 있으며 제어 변수에 값의 오름차순이 할당되는 동안 반복적으로 실행됩니다.

for 문의 일반 보기:

for <loop counter> := <start value> to <end value> do <statement>;

for 문이 실행을 시작하면 시작 값과 끝 값이 한 번 결정되고 이 값은 for 문이 실행되는 동안 유지됩니다. for 문의 본문에 포함된 문은 시작 값과 끝 값 사이의 범위에 있는 각 값에 대해 한 번씩 실행됩니다. 루프 카운터는 항상 초기 값으로 초기화됩니다. for 문이 실행 중일 때 루프 카운터의 값은 각 반복마다 증가합니다. 시작 값이 끝 값보다 크면 for 문의 본문에 포함된 명령문이 실행되지 않습니다. downto 키워드가 루프 문에서 사용되면 제어 변수의 값은 각 반복에서 XNUMX씩 감소합니다. 이러한 명령문의 시작 값이 끝 값보다 작으면 루프 문의 본문에 포함된 명령문이 실행되지 않습니다.

for 문의 본문에 포함된 문이 루프 카운터 값을 변경하면 오류입니다. for 문의 실행 후, for 문의 실행이 점프 문에 의해 중단되지 않는 한 제어 변수의 값은 정의되지 않습니다.

전제 조건이 있는 루프 문

전제 조건 루프 문(while 키워드로 시작)에는 문(복합 문일 수 있음)의 반복 실행을 제어하는 ​​식이 포함됩니다. 주기 모양:

B가 S를 하는 동안;

여기서 B는 논리 조건이며, 그 진실이 확인됩니다(루프를 종료하기 위한 조건).

S - 루프 본문 - 하나의 명령문.

명령문의 반복을 제어하는 ​​표현식은 부울 유형이어야 합니다. 내부 문이 실행되기 전에 평가됩니다. 표현식이 True로 평가되는 동안 내부 문은 반복적으로 실행됩니다. 표현식이 처음부터 False로 평가되면 전제 조건 루프 문에 포함된 문이 실행되지 않습니다.

사후 조건이 있는 루프 문

사후 조건이 있는 루프 문(repeat 단어로 시작)에서 명령문 시퀀스의 반복 실행을 제어하는 ​​표현식은 repeat 문 내에 포함됩니다. 주기 모양:

B까지 S를 반복합니다.

여기서 B는 논리 조건이며, 그 진실이 확인됩니다(루프를 종료하기 위한 조건).

S - 하나 이상의 루프 본문 문.

표현식의 결과는 부울 유형이어야 합니다. repeat 및 until 키워드 사이에 있는 명령문은 표현식의 결과가 True로 평가될 때까지 순차적으로 실행됩니다. 명령문 시퀀스의 각 실행 후에 표현식이 평가되기 때문에 명령문 시퀀스는 적어도 한 번 실행됩니다.

강의 № 3. 절차 및 기능

1. 보조 알고리즘의 개념

문제 해결 알고리즘은 전체 문제를 별도의 하위 작업으로 분해하여 설계되었습니다. 일반적으로 하위 작업은 하위 루틴으로 구현됩니다.

서브루틴은 매개변수라고 하는 일부 들어오는 양의 다른 값을 사용하여 기본 알고리즘에서 반복적으로 사용되는 보조 알고리즘입니다.

프로그래밍 언어의 서브루틴은 프로그램의 한 곳에서만 정의되고 작성된 일련의 명령문이지만 프로그램의 하나 이상의 지점에서 실행을 위해 호출될 수 있습니다. 각 서브루틴은 고유한 이름으로 식별됩니다.

파스칼에는 프로시저와 함수라는 두 가지 유형의 서브루틴이 있습니다. 프로시저와 함수는 선언과 명령문의 명명된 시퀀스입니다. 프로시저 또는 함수를 사용할 때 프로그램은 프로시저 또는 함수의 텍스트와 프로시저 또는 함수에 대한 호출을 포함해야 합니다. 설명에 지정된 매개변수를 형식이라고 하고 서브루틴 호출에 지정된 매개변수를 실제라고 합니다. 모든 형식 매개변수는 다음 범주로 나눌 수 있습니다.

1) 매개변수-변수;

2) 일정한 매개변수;

3) 매개변수 값;

4) 절차 매개변수 및 기능 매개변수, 즉 절차 유형 매개변수;

5) 유형화되지 않은 변수 매개변수.

절차 및 기능에 대한 텍스트는 절차 및 기능 설명 섹션에 있습니다.

프로시저 및 함수 이름을 매개변수로 전달

많은 문제, 특히 계산 수학에서 프로시저와 함수의 이름을 매개변수로 전달하는 것이 필요합니다. 이를 위해 TURBO PASCAL은 기술된 내용에 따라 절차적 또는 기능적이라는 새로운 데이터 유형을 도입했습니다. (절차 및 함수 유형은 유형 선언 섹션에 설명되어 있습니다.)

함수 및 프로시저 형식은 형식 매개변수 목록이 있지만 이름이 없는 프로시저 및 함수의 표제로 정의됩니다. 매개변수 없이 함수 또는 절차 유형을 정의할 수 있습니다. 예를 들면 다음과 같습니다.

유형

절차 = 절차;

절차적 또는 기능적 유형을 선언한 후에는 형식 매개변수(절차 및 함수의 이름)를 설명하는 데 사용할 수 있습니다. 또한 이름이 실제 매개변수로 전달될 실제 프로시저나 함수를 작성해야 합니다.

2. 파스칼에서의 절차

각 절차 설명에는 머리글과 프로그램 블록이 있습니다. 프로시저 헤더의 일반적인 형식은 다음과 같습니다.

프로시저 <이름> [(<형식 매개변수 목록>)];

프로시저는 프로시저 이름과 필수 매개변수가 포함된 프로시저 명령문으로 활성화됩니다. 프로시저가 실행될 때 실행될 명령문은 프로시저 모듈의 명령문 부분에 포함됩니다. 프로시저에 포함된 문이 프로시저 모듈 내의 프로시저 식별자를 사용하는 경우 프로시저는 재귀적으로 실행됩니다. 즉, 실행될 때 자신을 참조합니다.

3. 파스칼의 함수

함수 선언은 값이 계산되고 반환되는 프로그램 부분을 정의합니다. 함수 헤더의 일반적인 형식은 다음과 같습니다.

함수 <이름> [(<형식 매개변수 목록>)]: <반환 유형>;

함수는 호출될 때 활성화됩니다. 함수가 호출되면 함수 식별자와 평가에 필요한 매개변수가 지정됩니다. 함수 호출은 표현식에 피연산자로 포함될 수 있습니다. 표현식이 평가되면 함수가 실행되고 피연산자의 값은 함수에서 반환된 값이 됩니다.

기능 블록의 연산자 부분은 기능이 활성화될 때 실행되어야 하는 명령문을 지정합니다. 모듈에는 함수 식별자에 값을 할당하는 할당문이 하나 이상 있어야 합니다. 함수의 결과는 할당된 마지막 값입니다. 이러한 할당 문이 없거나 실행되지 않은 경우 함수의 반환 값은 정의되지 않습니다.

모듈 내에서 함수를 호출할 때 함수 식별자를 사용하면 함수가 재귀적으로 실행됩니다.

4. 서브루틴의 설명과 연결을 전달합니다. 지령

프로그램은 여러 서브루틴을 포함할 수 있습니다. 즉, 프로그램 구조가 복잡할 수 있습니다. 그러나 이러한 서브루틴은 동일한 중첩 수준에 있을 수 있으므로 특별한 전달 선언이 사용되지 않는 한 서브루틴 선언이 먼저 오고 다음에 호출되어야 합니다.

명령문 블록 대신 정방향 지시문을 포함하는 프로시저 선언을 정방향 선언이라고 합니다. 이 선언 이후 어딘가에 정의 선언으로 프로시저를 정의해야 합니다. 정의 선언은 동일한 프로시저 식별자를 사용하지만 형식 매개변수 목록을 생략하고 명령문 블록을 포함하는 선언입니다. 전방 선언과 정의 선언은 프로시저 및 함수 선언의 동일한 부분에 나타나야 합니다. 그들 사이에 전방 선언 절차를 참조할 수 있는 다른 절차와 함수를 선언할 수 있습니다. 따라서 상호 재귀가 가능합니다.

전방 설명 및 정의 설명은 절차에 대한 완전한 설명입니다. 절차는 전방 기술을 사용하여 기술된 것으로 간주됩니다.

프로그램에 상당히 많은 서브루틴이 포함되어 있으면 프로그램이 시각적으로 표시되지 않고 탐색하기 어려울 것입니다. 이를 방지하기 위해 일부 서브루틴은 디스크에 소스 파일로 저장되며, 필요에 따라 컴파일 단계에서 컴파일 지시문을 사용하여 메인 프로그램에 연결됩니다.

지시문은 일반 주석이 있을 수 있는 프로그램의 모든 위치에 배치할 수 있는 특수 주석입니다. 그러나 지시문에 특수 표기법이 있다는 점에서 다릅니다. 공백 없이 닫는 괄호 바로 뒤에 S 기호가 쓰여지고 다시 공백 없이 지시문이 표시됩니다.

1) {SE+} - 수학 보조 프로세서를 에뮬레이트합니다.

2) {SF+} - 먼 유형의 프로시저 및 함수 호출을 형성합니다.

3) {SN+} - 수학 보조 프로세서를 사용합니다.

4) {SR+} - 범위가 범위를 벗어났는지 확인합니다.

일부 컴파일 스위치에는 다음과 같은 매개변수가 포함될 수 있습니다.

{$1 file name} - 컴파일된 프로그램의 텍스트에 명명된 파일을 포함합니다.

강의 4. 서브루틴

1. 서브프로그램 매개변수

프로시저 또는 함수에 대한 설명은 형식 매개변수 목록을 지정합니다. 형식 매개변수 목록에 선언된 각 매개변수는 설명 중인 프로시저 또는 함수에 대해 로컬이며 해당 프로시저 또는 함수와 연결된 모듈의 식별자로 참조할 수 있습니다.

매개변수에는 값, 변수 및 유형이 지정되지 않은 변수의 세 가지 유형이 있습니다. 그것들은 다음과 같은 특징이 있습니다.

1. 선행 키워드가 없는 매개변수 그룹은 값 매개변수 목록입니다.

2. 앞에 const 키워드가 있고 뒤에 유형이 오는 매개변수 그룹은 상수 매개변수 목록입니다.

3. 앞에 var 키워드가 있고 뒤에 type이 오는 매개변수 그룹은 유형이 지정되지 않은 변수 매개변수의 목록입니다.

4. 앞에 var 또는 const 키워드가 있고 뒤에 유형이 없는 매개변수 그룹은 유형이 지정되지 않은 변수 매개변수 목록입니다.

2. 서브루틴 파라미터의 종류

값 매개변수

형식 값 매개변수는 프로시저 또는 함수가 호출될 때 해당 실제 매개변수에서 초기 값을 파생한다는 점을 제외하고는 프로시저 또는 함수에 대한 로컬 변수로 처리됩니다. 형식 값 매개변수가 수행하는 변경은 실제 매개변수의 값에 영향을 미치지 않습니다. 해당 실제 값 매개변수 값은 표현식이어야 하며 해당 값은 파일 유형 또는 파일 유형을 포함하는 구조 유형이 아니어야 합니다.

실제 매개변수는 형식 값 매개변수의 유형과 할당 호환 가능한 유형이어야 합니다. 매개변수가 문자열 유형이면 형식 매개변수의 크기 속성은 255입니다.

상수 매개변수

형식 상수 매개변수는 해당 실제 매개변수에서 프로시저나 함수가 호출될 때 값을 가져오는 읽기 전용 지역 변수와 같은 방식으로 작동합니다. 형식 상수 매개변수에 대한 할당은 허용되지 않습니다. 형식 상수 매개변수는 다른 프로시저나 함수에 실제 매개변수로 전달할 수도 없습니다. 프로시저 또는 함수 문의 실제 매개변수에 해당하는 상수 매개변수는 실제 매개변수 값과 동일한 규칙을 따라야 합니다.

프로시저나 함수가 실행될 때 형식 매개변수가 값을 변경하지 않는 경우 값 매개변수 대신 상수 매개변수를 사용해야 합니다. 상수 매개변수를 사용하면 형식 매개변수에 대한 우발적인 할당을 방지하기 위해 프로시저나 함수를 구현할 수 있습니다. 또한 구조체 및 문자열 유형 매개변수의 경우 컴파일러는 상수 매개변수에 대한 값 매개변수 대신 사용할 때 더 효율적인 코드를 생성할 수 있습니다.

변수 매개변수

변수 매개변수는 값이 프로시저나 함수에서 호출 프로그램으로 전달되어야 할 때 사용됩니다. 프로시저 또는 함수 호출 문의 해당 실제 매개 변수는 변수 참조여야 합니다. 프로시저나 함수가 호출되면 형식 매개변수 변수가 실제 변수로 대체되고 형식 매개변수 변수 값의 모든 변경 사항이 실제 매개변수에 반영됩니다.

프로시저 또는 함수 내에서 형식 변수 매개변수에 대한 참조는 실제 매개변수 자체에 대한 액세스로 이어집니다. 실제 매개변수의 유형은 형식 변수 매개변수의 유형과 일치해야 하지만 이 제한은 유형이 지정되지 않은 변수 매개변수를 사용하여 우회할 수 있습니다.

형식화되지 않은 매개변수

형식 매개변수가 유형이 지정되지 않은 변수 매개변수인 경우 해당 실제 매개변수는 유형에 관계없이 변수 또는 상수에 대한 참조가 될 수 있습니다. var 키워드로 선언된 형식화되지 않은 매개변수는 수정할 수 있지만 const 키워드로 선언된 형식화되지 않은 매개변수는 읽기 전용입니다.

프로시저나 함수에서 유형이 지정되지 않은 변수 매개변수에는 유형이 없습니다. 즉, 변수 유형 할당에 의해 특정 유형이 지정될 때까지 모든 유형의 변수와 호환되지 않습니다.

형식화되지 않은 매개변수가 더 많은 유연성을 제공하지만 사용과 관련된 몇 가지 위험이 있습니다. 컴파일러는 형식화되지 않은 변수에 대한 작업의 유효성을 확인할 수 없습니다.

절차적 변수

절차적 유형을 정의한 후에는 이 유형의 변수를 설명하는 것이 가능해집니다. 이러한 변수를 절차 변수라고 합니다. 정수형 값을 할당할 수 있는 정수 변수와 마찬가지로 절차형 변수에도 절차형 값을 할당할 수 있습니다. 물론 그러한 값은 또 다른 프로시저 변수일 수 있지만 프로시저 또는 함수 식별자일 수도 있습니다. 이 컨텍스트에서 프로시저 또는 함수의 선언은 값이 프로시저 또는 함수인 특수한 종류의 상수에 대한 설명으로 볼 수 있습니다.

다른 할당과 마찬가지로 왼쪽과 오른쪽의 변수 값은 할당 호환 가능해야 합니다. 할당 호환이 되려면 절차 유형의 매개변수 수가 같아야 하며 해당 위치의 매개변수 유형도 동일해야 합니다. 절차 형식 선언의 매개 변수 이름은 영향을 주지 않습니다.

또한 할당 호환성을 보장하기 위해 프로시저 또는 함수가 프로시저 변수에 할당되는 경우 다음 요구 사항을 충족해야 합니다.

1) 표준 절차나 기능이 아니어야 합니다.

2) 그러한 절차나 함수는 중첩될 수 없습니다.

3) 이러한 절차는 인라인 절차가 아니어야 합니다.

4) 인터럽트 절차가 아니어야 합니다.

표준 절차 및 기능은 Writeln, Readln, Chr, Ord와 같이 System 모듈에 설명된 절차 및 기능입니다. 프로시저 변수가 있는 중첩 프로시저 및 함수는 사용할 수 없습니다. 프로시저 또는 함수는 다른 프로시저 또는 함수 내에서 선언될 때 중첩된 것으로 간주됩니다.

절차적 유형의 사용은 절차적 변수에만 국한되지 않습니다. 다른 형식과 마찬가지로 절차 형식은 구조 형식 선언에 참여할 수 있습니다.

프로시저 변수에 프로시저 값이 할당되면 물리 계층에서 프로시저 주소가 변수에 저장됩니다. 실제로 프로시저 변수는 포인터 변수와 매우 유사하지만 데이터를 참조하는 대신 프로시저 또는 함수를 가리킵니다. 포인터와 마찬가지로 절차 변수는 메모리 주소를 포함하는 4바이트(XNUMX워드)를 차지합니다. 첫 번째 단어는 오프셋을 저장하고 두 번째 단어는 세그먼트를 저장합니다.

절차 유형 매개변수

프로시저 형식은 모든 컨텍스트에서 사용할 수 있으므로 프로시저 및 함수를 매개 변수로 사용하는 프로시저 또는 함수를 설명할 수 있습니다. 프로시저 형식 매개 변수는 여러 프로시저 또는 함수에 대해 몇 가지 일반적인 작업을 수행해야 할 때 특히 유용합니다.

프로시저 또는 함수가 매개변수로 전달되는 경우 할당과 동일한 유형 호환성 규칙을 따라야 합니다. 즉, 이러한 프로시저나 함수는 far 지시문으로 컴파일해야 하고 내장 함수가 될 수 없으며 중첩될 수 없으며 인라인 또는 인터럽트 속성으로 설명할 수 없습니다.

강의 #5. 문자열 데이터 유형

1. 파스칼의 문자열 유형

특정 길이의 문자 시퀀스를 문자열이라고 합니다. 문자열 유형의 변수는 변수 이름, 예약어 문자열을 지정하고 선택적으로(반드시 그렇지는 않음) 최대 크기, 즉 문자열 길이를 대괄호 안에 지정하여 정의됩니다. 최대 문자열 크기를 설정하지 않으면 기본적으로 255가 됩니다. 즉, 문자열은 255자로 구성됩니다.

문자열의 각 요소는 해당 번호로 참조할 수 있습니다. 그러나 문자열은 배열의 경우처럼 요소별로가 아니라 전체적으로 입력 및 출력됩니다. 입력한 문자 수는 최대 문자열 크기에 지정된 수를 초과하지 않아야 하므로 이러한 초과가 발생하는 경우 "추가" 문자는 무시됩니다.

2. 문자열형 변수의 프로시저와 함수

1. 함수 복사(S: 문자열; 인덱스, 개수: 정수): 문자열;

문자열의 하위 문자열을 반환합니다. S는 String 유형의 표현식입니다.

Index 및 Count는 정수 유형 표현식입니다. 이 함수는 인덱스 위치에서 시작하는 Count 문자를 포함하는 문자열을 반환합니다. Index가 S의 길이보다 크면 함수는 빈 문자열을 반환합니다.

2. 삭제 절차(var S: String; Index, Count: Integer);

인덱스 위치에서 시작하여 문자열 S에서 길이가 Count인 문자의 하위 문자열을 제거합니다. S는 String 유형의 변수입니다. Index 및 Count는 정수 유형 표현식입니다. Index가 S의 길이보다 크면 문자가 제거되지 않습니다.

3. 프로시저 삽입(소스: 문자열, var S: 문자열, 인덱스: 정수);

지정된 위치에서 시작하여 부분 문자열을 문자열로 연결합니다. 소스는 문자열 유형의 표현식입니다. S는 길이에 관계없이 String 유형의 변수입니다. 인덱스는 정수 유형의 표현식입니다. 삽입은 S[인덱스] 위치에서 시작하여 소스를 S에 삽입합니다.

4. 함수 길이(S: 문자열): 정수;

문자열 S에서 실제로 사용된 문자 수를 반환합니다. null로 끝나는 문자열을 사용할 때 문자 수가 반드시 바이트 수와 같지는 않습니다.

5. 함수 Pos(하위 문자열: 문자열; S: 문자열): 정수;

문자열에서 부분 문자열을 검색합니다. Pos는 S 내에서 Substr을 찾고 S 내에서 Substr의 첫 번째 문자 인덱스인 정수 값을 반환합니다. Substr을 찾을 수 없으면 Pos는 null을 반환합니다.

3. 녹음

레코드는 서로 다른 유형에 속하는 제한된 수의 논리적으로 관련된 구성 요소의 모음입니다. 레코드의 구성 요소를 필드라고 하며 각 구성 요소는 이름으로 식별됩니다. 레코드 필드에는 필드 이름과 필드 유형을 나타내는 콜론이 뒤따릅니다. 레코드 필드는 파일 유형을 제외하고 Pascal에서 허용되는 모든 유형이 될 수 있습니다.

파스칼 언어로 된 레코드에 대한 설명은 서비스 단어 RECORD를 사용하여 수행되고 그 뒤에 레코드의 구성 요소에 대한 설명이 따라옵니다. 항목에 대한 설명은 서비스 단어 END로 끝납니다.

예를 들어 노트북에는 성, 이니셜, 전화번호가 포함되어 있으므로 노트북에서 별도의 줄을 다음 항목으로 나타내는 것이 편리합니다.

유형 행 = 레코드

FIO: 문자열[20];

전화: 문자열[7];

끝;

var str: 행;

유형 이름을 사용하지 않고 레코드 설명도 가능합니다. 예를 들면 다음과 같습니다.

var str : 기록

FIO : 문자열[20];

전화 : 문자열[7];

끝;

레코드 전체를 참조하는 것은 할당 기호의 왼쪽과 오른쪽에 동일한 유형의 레코드 이름이 사용되는 할당문에서만 허용됩니다. 다른 모든 경우에는 별도의 기록 필드가 운영됩니다. 개별 레코드 구성 요소를 참조하려면 레코드 이름을 지정하고 점으로 구분하여 원하는 필드의 이름을 지정해야 합니다. 이러한 이름을 복합 이름이라고 합니다. 레코드 구성 요소는 레코드가 될 수도 있습니다. 이 경우 고유 이름에는 두 개가 아닌 더 많은 이름이 포함됩니다.

with 추가 연산자를 사용하여 참조 레코드 구성 요소를 단순화할 수 있습니다. 이를 통해 각 필드를 특징짓는 복합 이름을 필드 이름으로 바꾸고 조인 문에서 레코드 이름을 정의할 수 있습니다.

때때로 개별 레코드의 내용은 해당 필드 중 하나의 값에 따라 다릅니다. Pascal 언어에서는 공통 부분과 변형 부분으로 구성된 레코드 설명이 허용됩니다. 변형 부분은 구조의 대소문자 P를 사용하여 지정됩니다. 여기서 P는 레코드의 공통 부분에 있는 필드 이름입니다. 이 필드에서 허용되는 가능한 값은 변형 문과 동일한 방식으로 나열됩니다. 그러나 변형 문에서와 같이 수행할 작업을 지정하는 대신 변형 필드가 괄호 안에 지정됩니다. 변형 부품에 대한 설명은 서비스 단어 end로 끝납니다. 필드 유형 P는 변형 부품의 제목에 지정할 수 있습니다. 레코드는 유형이 지정된 상수를 사용하여 초기화됩니다.

4. 세트

파스칼 언어에서 집합의 개념은 집합의 수학적 개념을 기반으로 합니다. 다른 요소의 제한된 모음입니다. 열거형 또는 간격 데이터 형식은 구체적인 집합 형식을 구성하는 데 사용됩니다. 집합을 구성하는 요소의 유형을 기본 유형이라고 합니다.

다중 유형은 기능 단어 세트를 사용하여 설명됩니다. 예를 들면 다음과 같습니다.

유형 M = B 세트;

여기서 M은 복수형이고 B는 기본형입니다.

복수형에 대한 변수의 소속은 변수 선언 섹션에서 직접 결정할 수 있습니다.

집합 유형 상수는 쉼표로 구분된 기본 유형의 요소 또는 범위의 대괄호 시퀀스로 작성됩니다. [] 형식의 상수는 빈 부분 집합을 의미합니다.

집합에는 기본 유형의 요소 집합, 지정된 집합의 모든 하위 집합 및 빈 하위 집합이 포함됩니다. 집합이 구축된 기본 유형에 K개의 요소가 있는 경우 이 집합에 포함된 하위 집합의 수는 2의 K 거듭제곱과 같습니다. 기본 유형의 요소가 상수에 나열되는 순서는 무관합니다. . 다중 유형의 변수 값은 [T] 형식의 구성으로 제공될 수 있습니다. 여기서 T는 기본 유형의 변수입니다.

대입(:=), 합집합(+), 교집합(*), 빼기(-) 연산은 집합 유형의 변수와 상수에 적용할 수 있습니다. 이러한 작업의 결과는 복수 유형의 값입니다.

1) ['A','B'] + ['A','D']는 ['A','B','D']를 줄 것입니다.

2) ['A'] * ['A','B','C']는 ['A']를 줄 것입니다.

3) ['A','B','C'] - ['A','B']는 ['C']를 줍니다.

다음 작업은 여러 값에 적용할 수 있습니다. 동일성(=), 비 동일성(<>), 포함(<=), 포함(>=). 이러한 작업의 결과에는 부울 유형이 있습니다.

1) ['A','B'] = ['A','C'] FALSE ;

2) ['A','B'] <> ['A','C']는 TRUE를 제공합니다.

3) ['B'] <= ['B','C']는 TRUE를 제공합니다.

4) ['C','D'] >= ['A']는 FALSE를 제공합니다.

이러한 연산 외에도 집합 유형의 값으로 작업하기 위해 연산 기호 왼쪽에 있는 기본 유형의 요소가 연산 기호 오른쪽에 있는 집합에 속하는지 확인하는 in 연산이 사용됩니다. . 이 작업의 결과는 부울입니다. 요소가 집합에 속하는지 확인하는 연산은 관계형 연산 대신 자주 사용됩니다.

여러 데이터 유형이 프로그램에서 사용되는 경우 데이터의 비트 문자열에 대해 작업이 수행됩니다. 컴퓨터 메모리에 있는 다중 유형의 각 값은 하나의 이진수에 해당합니다.

여러 유형의 값은 I/O 목록의 요소가 될 수 없습니다. Pascal 언어에서 컴파일러를 구체적으로 구현할 때마다 집합이 빌드되는 기본 유형의 요소 수가 제한됩니다.

여러 유형 값의 초기화는 유형이 지정된 상수를 사용하여 수행됩니다.

다음은 세트 작업을 위한 몇 가지 절차입니다.

1. 절차 제외(var S: T의 집합; I:T);

집합 S에서 요소 I을 제거합니다. S는 "set" 유형의 변수이고 I은 원래 유형 S와 호환되는 유형의 표현식입니다. Exclude(S, I)는 S : = S - [I], 그러나 더 효율적인 코드를 생성합니다.

2. 절차 Include(var S: T의 집합; I:T);

집합 S에 요소 I을 추가합니다. S는 "set" 유형의 변수이고 I은 유형 S와 호환되는 유형의 표현식입니다. Include(S, I) 구문은 S : = S + [ I], 그러나 더 효율적인 코드를 생성합니다.

강의 6. 파일

1. 파일. 파일 작업

Pascal 언어에 파일 형식이 도입된 이유는 입력, 출력 및 데이터 저장을 위해 설계된 주변(외부) 컴퓨터 장치와 함께 작업할 수 있는 기능을 제공해야 하기 때문입니다.

파일 데이터 유형(또는 파일)은 동일한 유형의 임의 수의 구성요소에 대한 정렬된 컬렉션을 정의합니다. 배열, 집합 및 레코드의 공통 속성은 구성 요소의 수는 프로그램 작성 단계에서 결정되지만 프로그램 텍스트의 파일 구성 요소 수는 결정되지 않고 임의적일 수 있다는 것입니다.

파일 작업 시 I/O 작업이 수행됩니다. 입력 작업은 외부 장치(입력 파일에서)에서 컴퓨터의 메인 메모리로 데이터를 전송하는 것을 의미하고, 출력 작업은 메인 메모리에서 외부 장치로(출력 파일로) 데이터를 전송하는 것입니다. 외부 장치의 파일은 종종 실제 파일이라고 합니다. 이름은 운영 체제에 의해 결정됩니다.

Pascal 프로그램에서 파일 이름은 문자열을 사용하여 지정됩니다. 프로그램에서 파일로 작업하려면 파일 변수를 정의해야 합니다. Pascal은 텍스트 파일, 구성 요소 파일, 형식이 지정되지 않은 파일의 세 가지 파일 형식을 지원합니다.

프로그램에서 선언된 파일 변수를 논리 파일이라고 합니다. I/O 데이터를 제공하는 모든 기본 절차와 기능은 논리 파일에서만 작동합니다. 파일 열기 절차를 수행하려면 먼저 실제 파일이 논리 파일과 연관되어야 합니다.

텍스트 파일

Pascal 언어의 특별한 위치는 텍스트 파일로 채워지며 구성 요소는 문자 유형입니다. 텍스트 파일을 설명하기 위해 언어는 표준 유형 텍스트를 정의합니다.

var TF1, TF2: 텍스트;

텍스트 파일은 일련의 행이고 행은 일련의 문자입니다. 줄은 길이가 가변적이며 각 줄은 줄 종결자로 끝납니다.

구성 요소 파일

구성 요소 또는 유형이 지정된 파일은 해당 구성 요소의 유형이 선언된 파일입니다. 구성 요소 파일은 변수 값의 기계 표현으로 구성되며 컴퓨터 메모리와 동일한 형식으로 데이터를 저장합니다.

파일 형식 값에 대한 설명은 다음과 같습니다.

유형 M = T의 파일;

여기서 M은 파일 형식의 이름입니다.

T - 구성 요소 유형.

파일 구성 요소는 모든 스칼라 유형이 될 수 있으며 구조화된 유형(배열, 집합, 레코드)이 될 수 있습니다. Pascal 언어의 거의 모든 특정 구현에서 "파일 파일" 구성은 허용되지 않습니다.

구성 요소 파일에 대한 모든 작업은 표준 절차를 사용하여 수행됩니다.

쓰기(f,X1,X2,...XK)

형식이 지정되지 않은 파일

형식이 지정되지 않은 파일을 사용하면 컴퓨터 메모리의 임의 섹션을 디스크에 쓰고 디스크에서 메모리로 읽을 수 있습니다. 형식화되지 않은 파일은 다음과 같이 설명됩니다.

var f: 파일;

이제 다양한 유형의 파일 작업을 위한 절차와 기능을 나열합니다.

1. 프로시저 할당(var F; 파일 이름: 문자열);

AssignFile 프로시저는 외부 파일 이름을 파일 변수에 매핑합니다.

F는 모든 파일 유형의 파일 변수이고, FileName은 String 표현식이거나 확장 구문이 허용되는 경우 PChar 표현식입니다. F를 사용한 모든 추가 작업은 외부 파일로 수행됩니다.

이미 열려 있는 파일 변수와 함께 프로시저를 사용할 수 없습니다.

2. 절차 닫기(varF);

프로시저는 파일 변수와 외부 디스크 파일 간의 링크를 끊고 파일을 닫습니다.

F는 Reset, Rewrite 또는 Append 프로시저에 의해 열리는 모든 파일 유형의 파일 변수입니다. F와 연관된 외부 파일은 완전히 수정된 다음 닫혀서 재사용을 위해 파일 설명자가 해제됩니다.

{SI+} 지시문을 사용하면 예외 처리를 사용하여 프로그램 실행 중 오류를 처리할 수 있습니다. {$1-} 지시문을 끈 상태에서 IOResult를 사용하여 I/O 오류를 확인해야 합니다.

3. 함수 Eof(var F): 부울;

{유형 또는 유형이 지정되지 않은 파일}

함수 Eof[(var F: Text)]: 부울;

{텍스트 파일}

현재 파일 위치가 파일의 끝인지 여부를 확인합니다.

Eof(F)는 현재 파일 위치가 파일의 마지막 문자 뒤에 있거나 파일이 비어 있으면 True를 반환합니다. 그렇지 않으면 Eof(F)는 False를 반환합니다.

{SI+} 지시문을 사용하면 예외 처리를 사용하여 프로그램 실행 중 오류를 처리할 수 있습니다. {SI-} 지시문이 꺼져 있으면 IOResult를 사용하여 I/O 오류를 확인해야 합니다.

4. 절차 지우기(var F);

F와 연결된 외부 파일을 삭제합니다.

F는 모든 파일 유형의 파일 변수입니다.

Erase 프로시저를 호출하기 전에 파일을 닫아야 합니다.

{SI+} 지시문을 사용하면 예외 처리를 사용하여 프로그램 실행 중 오류를 처리할 수 있습니다. {SI-} 지시문이 꺼져 있으면 IOResult를 사용하여 I/O 오류를 확인해야 합니다.

5. 함수 FileSize(var F): 정수;

파일 F의 크기를 바이트 단위로 반환합니다. 그러나 F가 형식화된 파일인 경우 FileSize는 파일의 레코드 수를 반환합니다. FileSize 함수를 사용하기 전에 파일을 열어야 합니다. 파일이 비어 있으면 FileSize(F)는 XNUMX을 반환합니다. F는 모든 파일 유형의 변수입니다.

6. 함수 FilePos(varF): LongInt;

파일 내에서 파일의 현재 위치를 반환합니다.

FilePos 기능을 사용하기 전에 파일이 열려 있어야 합니다. FilePos 함수는 텍스트 파일과 함께 사용되지 않습니다. F는 텍스트 유형을 제외한 모든 파일 유형의 변수입니다.

7. 프로시저 재설정(var F [: 파일; RecSize: Word]);

기존 파일을 엽니다.

F는 AssignFile을 사용하여 외부 파일과 연결된 모든 파일 유형의 변수입니다. RecSize는 F가 형식화되지 않은 파일인 경우 사용되는 선택적 표현식입니다. F가 형식화되지 않은 파일인 경우 RecSize는 데이터를 전송할 때 사용되는 레코드 크기를 결정합니다. RecSize를 생략하면 기본 레코드 크기는 128바이트입니다.

재설정 절차는 파일 변수 F와 연결된 기존 외부 파일을 엽니다. 해당 이름을 가진 외부 파일이 없으면 런타임 오류가 발생합니다. F와 연결된 파일이 이미 열려 있으면 먼저 닫았다가 다시 엽니다. 현재 파일 위치는 파일의 시작 부분으로 설정됩니다.

8. 프로시저 Rewrite(var F: File [; Recsize: Word]);

새 파일을 만들고 엽니다.

F는 AssignFile을 사용하여 외부 파일과 연결된 모든 파일 유형의 변수입니다. RecSize는 F가 형식화되지 않은 파일인 경우 사용되는 선택적 표현식입니다. F가 형식화되지 않은 파일인 경우 RecSize는 데이터를 전송할 때 사용되는 레코드 크기를 결정합니다. RecSize를 생략하면 기본 레코드 크기는 128바이트입니다.

Rewrite 절차는 F와 연결된 이름으로 새 외부 파일을 만듭니다. 동일한 이름을 가진 외부 파일이 이미 있으면 삭제되고 새 빈 파일이 만들어집니다.

9. 프로시저 Seek(var F; N: LongInt);

현재 파일 위치를 지정된 구성 요소로 이동합니다. 형식 또는 형식이 지정되지 않은 파일이 열려 있는 경우에만 절차를 사용할 수 있습니다.

파일 F의 현재 위치가 N으로 이동합니다. 파일의 첫 번째 구성 요소의 번호는 0입니다.

Seek(F, FileSize(F)) 명령은 현재 파일 위치를 파일 끝으로 이동합니다.

10. 프로시저 Append(var F: Text);

기존 텍스트 파일을 열어 파일 끝에 정보를 추가합니다(추가).

주어진 이름의 외부 파일이 존재하지 않으면 런타임 오류가 발생합니다. 파일 F가 이미 열려 있으면 닫았다가 다시 엽니다. 현재 파일 위치는 파일의 끝으로 설정됩니다.

11. 함수 Eoln[(var F: Text)]: Boolean;

현재 파일 위치가 텍스트 파일의 줄 끝인지 확인합니다.

Eoln(F)는 현재 파일 위치가 줄이나 파일의 끝에 있으면 True를 반환합니다. 그렇지 않으면 Eoln(F)는 False를 반환합니다.

12. 절차 읽기(F, V1 [, V2,..., Vn]);

{유형이 지정된 파일과 유형이 지정되지 않은 파일}

프로시저 읽기([var F: Text;] V1 [, V2,..., Vn]);

{텍스트 파일}

형식화된 파일의 경우 프로시저는 파일 구성 요소를 변수로 읽습니다. 읽을 때마다 파일의 현재 위치가 다음 요소로 이동합니다.

텍스트 파일의 경우 하나 이상의 값을 하나 이상의 변수로 읽습니다.

String Read 유형의 변수를 사용하면 다음 줄 끝 표시자까지 또는 Eof(F)가 True로 평가될 때까지(포함하지 않음) 모든 문자를 읽습니다. 결과 문자열이 변수에 할당됩니다.

정수 또는 실수 유형의 변수의 경우 프로시저는 파스칼 구문의 규칙에 따라 숫자를 형성하는 일련의 문자를 기다립니다. 첫 번째 공백, 탭 또는 줄 끝이 발생하거나 Eof(F)가 True로 평가되면 읽기가 중지됩니다. 숫자 문자열이 예상 형식과 일치하지 않으면 I/O 오류가 발생합니다.

13. 절차 Readln([var F: Text;] V1 [, V2..., Vn]);

Read 프로시저의 확장이며 텍스트 파일에 대해 정의됩니다. 줄 끝 표시자를 포함하여 파일에서 문자열을 읽고 다음 줄의 시작 부분으로 이동합니다. 매개변수 없이 Readln(F) 함수를 호출하면 현재 파일 위치가 다음 행의 시작 부분으로 이동합니다(있는 경우). 그렇지 않으면 파일 끝으로 이동합니다.

14. 함수 SeekEof[(var F: Text)]: 부울;

파일 끝을 반환하고 열린 텍스트 파일에만 사용할 수 있습니다. 일반적으로 텍스트 파일에서 숫자 값을 읽는 데 사용됩니다.

15. 함수 SeekEoln[(var F: Text)]: Boolean;

파일의 줄 종결자를 반환하며 열린 텍스트 파일에만 사용할 수 있습니다. 일반적으로 텍스트 파일에서 숫자 값을 읽는 데 사용됩니다.

16. 프로시저 쓰기([var F: Text;] P1 [, P2,..., Pn]);

{텍스트 파일}

텍스트 파일에 하나 이상의 값을 씁니다.

각 입력 매개변수는 Char 유형, 정수 유형(Byte, ShortInt, Word, Longint, Cardinal) 중 하나, 부동 소수점 유형(Single, Real, Double, Extended, Currency) 중 하나, 문자열 유형( PChar, AisiString, ShortString) 또는 부울 유형(Boolean, Bool) 중 하나입니다.

프로시저 쓰기(F, V1,..., Vn);

{입력된 파일}

파일 구성 요소에 변수를 씁니다. 변수 VI...., Vn은 파일 요소와 동일한 유형이어야 합니다. 변수가 기록될 때마다 파일의 현재 위치가 다음 요소로 이동됩니다.

17. 절차 Writeln([var F: Text;] [P1, P2,..., Pn]);

{텍스트 파일}

쓰기 작업을 수행한 다음 파일에 줄 끝 마커를 배치합니다.

매개변수 없이 Writeln(F)를 호출하면 파일에 줄 끝 마커가 기록됩니다. 파일은 출력을 위해 열려 있어야 합니다.

2. 모듈. 모듈 유형

Pascal의 모듈(1Ж1Т)은 특별히 설계된 서브루틴 라이브러리입니다. 모듈은 프로그램과 달리 자체적으로 시작할 수 없으며 프로그램 및 기타 모듈 구축에만 참여할 수 있습니다. 모듈을 사용하면 절차 및 기능의 개인 라이브러리를 만들고 거의 모든 크기의 프로그램을 빌드할 수 있습니다.

Pascal의 모듈은 별도로 저장되고 독립적으로 컴파일된 프로그램 단위입니다. 일반적으로 모듈은 다른 프로그램에서 사용하기 위한 소프트웨어 리소스 모음입니다. 프로그램 리소스는 상수, 유형, 변수, 서브루틴과 같은 Pascal 언어의 모든 요소로 이해됩니다. 모듈 자체는 실행 가능한 프로그램이 아니며 해당 요소는 다른 프로그램 단위에서 사용됩니다.

모듈의 모든 프로그램 요소는 두 부분으로 나눌 수 있습니다.

1) 다른 프로그램이나 모듈에서 사용하도록 의도된 프로그램 요소, 이러한 요소는 모듈 외부에서 볼 수 있다고 합니다.

2) 모듈 자체의 작동에만 필요한 소프트웨어 요소를 보이지 않는(또는 숨겨진)라고 합니다.

이에 따라 모듈에는 헤더 외에 인터페이스, 실행 가능 및 초기화라는 세 가지 주요 부분이 포함됩니다.

일반적으로 모듈의 구조는 다음과 같습니다.

단위 <모듈 이름>; {모듈 제목}

인터페이스

{모듈의 보이는 프로그램 요소에 대한 설명}

이행

{모듈의 숨겨진 프로그래밍 요소에 대한 설명}

시작하다

{모듈 요소 초기화 문}

끝.

특정 경우에 모듈은 구현 부분과 초기화 부분을 포함하지 않을 수 있으며 모듈 구조는 다음과 같습니다.

단위 <모듈 이름>; {모듈 제목}

인터페이스

{모듈의 보이는 프로그램 요소에 대한 설명}

이행

끝.

모듈에서 프로시저와 기능을 사용하는 것은 고유한 특성이 있습니다. 서브루틴 헤더에는 이름, 매개변수 목록 및 유형, 함수의 결과 유형과 같이 이를 호출하는 데 필요한 모든 정보가 포함되어 있습니다. 이 정보는 다른 프로그램 및 모듈에서 사용할 수 있어야 합니다. 반면에 알고리즘을 구현하는 서브루틴의 텍스트는 다른 프로그램 및 모듈에서 사용할 수 없습니다. 따라서 모듈의 인터페이스 부분에 프로시저 및 함수의 제목을 배치하고 구현 부분에 텍스트를 배치합니다.

모듈의 인터페이스 부분에는 프로시저 및 기능의 가시적(다른 프로그램 및 모듈에서 액세스 가능) 헤더(서비스 워드 포워드 없음)만 포함됩니다. 프로시저 또는 함수의 전체 텍스트는 구현 부분에 배치되며 헤더에는 형식 매개변수 목록이 포함될 수 없습니다.

모듈의 소스 코드는 컴파일 하위 메뉴의 Make 지시문을 사용하여 컴파일하고 디스크에 기록해야 합니다. 모듈 컴파일의 결과는 확장자가 . TPU(터보 파스칼 단위). 모듈의 기본 이름은 모듈의 헤더에서 가져옵니다.

모듈을 프로그램에 연결하려면 모듈 설명 섹션에 모듈 이름을 지정해야 합니다. 예를 들면 다음과 같습니다.

Crt, 그래프를 사용합니다.

모듈의 인터페이스 부분과 이 모듈을 사용하는 프로그램에서 변수의 이름이 동일한 경우 참조는 프로그램에서 설명하는 변수입니다. 모듈에서 선언된 변수를 참조하려면 모듈 이름과 변수 이름으로 구성된 복합 이름을 점으로 구분하여 사용해야 합니다. 복합 이름의 사용은 변수 이름뿐만 아니라 모듈의 인터페이스 부분에서 선언된 모든 이름에 적용됩니다.

모듈을 재귀적으로 사용하는 것은 금지되어 있습니다.

모듈에 초기화 섹션이 있는 경우 해당 섹션의 명령문은 해당 모듈을 사용하는 프로그램이 실행을 시작하기 전에 실행됩니다.

모듈의 유형을 나열해 보겠습니다.

1. 시스템 모듈.

SYSTEM 모듈은 I/O, 문자열 조작, 부동 소수점 연산 및 동적 메모리 할당과 같은 모든 내장 기능에 대한 하위 수준 지원 루틴을 구현합니다.

SYSTEM 모듈에는 모든 표준 및 내장 파스칼 루틴과 기능이 포함되어 있습니다. 표준 파스칼의 일부가 아니고 다른 모듈에서 찾을 수 없는 모든 파스칼 서브루틴은 시스템 모듈에 포함됩니다. 이 모듈은 모든 프로그램에서 자동으로 사용되며 uses 문에서 지정할 필요가 없습니다.

2. DOS 모듈.

Dos 모듈은 GetTime, SetTime, DiskSize 등과 같이 가장 일반적으로 사용되는 DOS 호출에 해당하는 수많은 Pascal 루틴 및 기능을 구현합니다.

3. CRT 모듈.

CRT 모듈은 화면 모드 제어, 확장된 키보드 코드, 색상, 창 및 소리와 같은 PC 기능에 대한 완전한 제어를 제공하는 여러 강력한 프로그램을 구현합니다. CRT 모듈은 개인용 컴퓨터 IBM PC, PC AT, IBM의 PS/2에서 실행되는 프로그램에서만 사용할 수 있으며 완전히 호환됩니다.

CRT 모듈을 사용하는 주요 이점 중 하나는 화면 작업을 수행할 때 더 빠르고 유연하다는 것입니다. CRT 모듈에서 작동하지 않는 프로그램은 DOS 운영 체제를 사용하여 화면에 정보를 표시하므로 추가 오버헤드가 발생합니다. CRT 모듈을 사용할 때 출력 정보는 BIOS(Basic Input/Output System)로 직접 전송되거나 더 빠른 작업을 위해 비디오 메모리로 직접 전송됩니다.

4. 그래프 모듈.

이 모듈에 포함된 절차와 기능을 이용하여 화면에 다양한 그래픽을 생성할 수 있습니다.

5. 오버레이 모듈.

OVERLAY 모듈을 사용하면 리얼 모드 DOS 프로그램의 메모리 요구 사항을 줄일 수 있습니다. 실제로 한 번에 프로그램의 일부만 메모리에 있기 때문에 사용 가능한 총 메모리 양을 초과하는 프로그램을 작성할 수 있습니다.

강의 7. 동적 메모리

1. 참조 데이터 유형. 동적 메모리. 동적 변수

정적 변수(정적으로 할당된)는 프로그램에서 명시적으로 선언된 변수이며 이름으로 참조됩니다. 정적 변수를 배치하기 위한 메모리의 위치는 프로그램이 컴파일될 때 결정됩니다. 이러한 정적 변수와 달리 Pascal 프로그램은 동적 변수를 생성할 수 있습니다. 동적 변수의 주요 속성은 프로그램 실행 중에 생성되고 메모리가 할당된다는 것입니다.

동적 변수는 동적 메모리 영역(힙 영역)에 배치됩니다. 동적 변수는 변수 선언에 명시적으로 지정되지 않으며 이름으로 참조할 수 없습니다. 이러한 변수는 포인터와 참조를 사용하여 액세스됩니다.

참조 유형(포인터)은 기본 유형이라고 하는 특정 유형의 동적 변수를 가리키는 값 집합을 정의합니다. 참조 유형 변수는 메모리에 있는 동적 변수의 주소를 포함합니다. 기본 유형이 선언되지 않은 식별자인 경우 포인터 유형과 동일한 유형 선언 부분에서 선언해야 합니다.

예약어 nil은 아무 것도 가리키지 않는 포인터 값을 가진 상수를 나타냅니다.

동적 변수에 대한 설명의 예를 들어 보겠습니다.

var p1, p2 : ^실제;

p3, p4 : ^정수;

2. 동적 메모리 작업. 형식화되지 않은 포인터

동적 메모리 프로시저 및 함수

1. 프로시저 새로 만들기(var p: 포인터).

동적 변수 p를 수용하기 위해 동적 메모리 영역에 공간을 할당합니다.Л, 그리고 그 주소를 포인터 p에 할당합니다.

2. Dispose(varp: 포인터) 프로시저.

New 프로시저에 의해 동적 변수 할당을 위해 할당된 메모리를 해제하고 포인터 p의 값은 정의되지 않습니다.

3. 프로시저 GetMem(varp: 포인터, 크기: Word).

힙 영역에 메모리 섹션을 할당하고 시작 주소를 포인터 p에 할당하고 섹션 크기(바이트)는 size 매개변수에 의해 지정됩니다.

4. 프로시저 FreeMem(var p: 포인터, 크기: 워드).

시작 주소가 p 포인터로 지정되고 크기가 size 매개변수로 지정되는 메모리 영역을 해제합니다. 포인터 p의 값은 정의되지 않습니다.

5. 프로시저 마크(var p: 포인터)

호출 시 사용 가능한 동적 메모리 섹션의 시작 주소를 포인터 p에 씁니다.

6. 프로시저 해제(var p: 포인터)

Mark 프로시저에 의해 포인터 p에 기록된 주소에서 시작하여 동적 메모리 섹션을 해제합니다. 즉, Mark 프로시저를 호출한 후 점유되었던 동적 메모리를 지웁니다.

7. MaxAvaikLongint 함수

가장 긴 여유 힙의 길이를 바이트 단위로 반환합니다.

8. MemAvaikLongint 함수

사용 가능한 동적 메모리의 총량을 바이트 단위로 반환합니다.

9. 도우미 함수 SizeOf(X):Word

X가 차지하는 바이트의 양을 반환합니다. 여기서 X는 모든 유형의 변수 이름 또는 유형 이름일 수 있습니다.

기본 제공 유형 포인터는 유형이 지정되지 않은 포인터, 즉 특정 유형을 가리키지 않는 포인터를 나타냅니다. 포인터 유형의 변수는 역참조될 수 있습니다. 이러한 변수 뒤에 ^ 문자를 지정하면 오류가 발생합니다.

nil로 표시된 값과 마찬가지로 포인터 값은 다른 모든 포인터 유형과 호환됩니다.

강의 № 8. 추상 데이터 구조

1. 추상 데이터 구조

배열, 집합 및 레코드와 같은 구조화된 데이터 유형은 프로그램이 전체 실행되는 동안 크기가 변경되지 않기 때문에 정적 구조입니다.

데이터 구조는 문제를 해결하는 과정에서 크기를 변경해야 하는 경우가 많습니다. 이러한 데이터 구조를 동적이라고 합니다. 여기에는 스택, 대기열, 목록, 트리 등이 포함됩니다.

배열, 레코드 및 파일을 사용한 동적 구조의 설명은 컴퓨터 메모리의 낭비를 초래하고 문제 해결 시간을 증가시킵니다.

모든 동적 구조의 각 구성 요소는 "포인터" 유형의 필드 하나와 데이터 배치용 필드 등 최소 두 개의 필드를 포함하는 레코드입니다. 일반적으로 레코드에는 하나가 아니라 여러 포인터와 여러 데이터 필드가 포함될 수 있습니다. 데이터 필드는 변수, 배열, 집합 또는 레코드일 수 있습니다.

포인팅 부분에 목록의 한 요소 주소가 포함되어 있으면 목록을 단방향(또는 단일 연결)이라고 합니다. 두 개의 구성 요소가 포함되어 있으면 이중으로 연결됩니다. 목록에서 다음과 같은 다양한 작업을 수행할 수 있습니다.

1) 목록에 요소 추가

2) 주어진 키를 사용하여 목록에서 요소를 제거합니다.

3) 키 필드의 주어진 값으로 요소를 검색합니다.

4) 목록의 요소를 정렬합니다.

5) 목록을 둘 이상의 목록으로 나누는 것

6) 둘 이상의 목록을 하나로 결합하는 것;

7) 기타 작업.

그러나 일반적으로 다양한 문제를 해결하기 위한 모든 작업의 ​​필요성은 발생하지 않습니다. 따라서 적용해야 하는 기본 작업에 따라 다양한 유형의 목록이 있습니다. 이들 중 가장 인기 있는 것은 스택과 큐입니다.

2. 스택

스택은 스택의 맨 위라고 하는 한쪽 끝에서 구성 요소를 추가하고 제거하는 동적 데이터 구조입니다. 스택은 LIFO(후입선출) - "후입선출"의 원칙에 따라 작동합니다.

일반적으로 스택에는 세 가지 작업이 수행됩니다.

1) 스택의 초기 형성(첫 번째 구성 요소의 기록);

2) 스택에 컴포넌트를 추가하는 단계;

3) 구성 요소 선택(삭제).

스택을 형성하고 작업하려면 "포인터" 유형의 두 변수가 있어야 합니다. 첫 번째 변수는 스택의 맨 위를 결정하고 두 번째 변수는 보조입니다.

예시. 스택을 형성하고 임의의 수의 구성 요소를 추가한 다음 모든 구성 요소를 읽고 디스플레이 화면에 표시하는 프로그램을 작성하십시오. 문자열을 데이터로 가져옵니다. 데이터 입력 - 키보드에서 입력 끝의 표시 - 문자열 END.

프로그램 스택;

Crt를 사용합니다.

유형

알파 = 문자열[10];

PComp = ^Comp;

Comp = 레코드

SD : 알파

pNext : 피컴

끝;

였다

pTop: PComp;

sc: 알파;

프로시저 스택 생성(var pTop : PComp; var sC : 알파);

시작하다

신규(pTop);

pTop^.pNext := NIL;

pTop^.sD := sC;

끝;

추가 ProcedureComp(var pTop : PComp; var sC : Alfa);

var pAux : PComp;

시작하다

신규(보조);

pAux^.pNext := pTop;

pTop := pAux;

pTop^.sD := sC;

끝;

프로시저 DelComp(var pTop: PComp; var sC: ALFA);

시작하다

sC := pTop^.sD;

pTop := pTop^.pNext;

끝;

시작하다

Clrscr;

writeln(' 문자열 입력 ');

readln(sC);

CreateStack(pTop, sc);

반복

writeln(' 문자열 입력 ');

readln(sC);

AddComp(pTop, sc);

sC = '종료'까지;

writeln('****** 출력 ******');

반복

DelComp(pTop, sc);

writeln(sC);

pTop = NIL이 될 때까지;

끝.

3. 대기열

큐는 한 쪽 끝에서 구성 요소가 추가되고 다른 쪽 끝에서 검색되는 동적 데이터 구조입니다. 대기열은 FIFO(선입선출) - "선입선출" 원칙에 따라 작동합니다.

대기열을 만들고 작업하려면 포인터 유형의 세 가지 변수가 필요합니다. 그 중 첫 번째는 대기열의 시작을 결정하고 두 번째는 대기열의 끝, 세 번째는 보조를 결정합니다.

예시. 큐를 구성하고 임의의 수의 구성 요소를 추가한 다음 모든 구성 요소를 읽고 디스플레이 화면에 표시하는 프로그램을 작성하십시오. 문자열을 데이터로 가져옵니다. 데이터 입력 - 키보드에서 입력 끝의 표시 - 문자열 END.

프로그램 대기열;

Crt를 사용합니다.

유형

알파 = 문자열[10];

PComp = ^Comp;

Comp = 레코드

SD : 알파

p다음: PComp;

끝;

였다

pBegin, pEnd : PComp;

sc: 알파;

Create ProcedureQueue(var pBegin,pEnd:PComp; var sC:Alfa);

시작하다

신규(pBegin);

pBegin^.pNext := NIL;

pBegin^.sD := sC;

pEnd := pBegin;

끝;

프로시저 추가 ProcedureQueue(var pEnd : PComp; var sC : Alfa);

var pAux : PComp;

시작하다

신규(pAux);

pAux^.pNext := NIL;

pEnd^.pNext := pAux;

pEnd := pAux;

pEnd^.sD := sC;

끝;

프로시저 DelQueue(var pBegin : PComp; var sC : 알파);

시작하다

sC := pBegin^.sD;

pBegin := pBegin^.pNext;

끝;

시작하다

Clrscr;

writeln(' 문자열 입력 ');

readln(sC);

CreateQueue(pBegin, pEnd, sc);

반복

writeln(' 문자열 입력 ');

readln(sC);

AddQueue(pEnd, sc);

sC = '종료'까지;

writeln(' ***** 결과 표시 *****');

반복

DelQueue(pBegin, sc);

writeln(sC);

pBegin = NIL까지;

끝.

강의 9. 트리와 같은 데이터 구조

1. 트리 데이터 구조

트리와 같은 데이터 구조는 소스와 생성된 사이의 연결인 관계가 있는 유한한 요소 노드 집합입니다.

N. Wirth가 제안한 재귀적 정의를 사용하면 기본 유형이 t인 트리 데이터 구조는 빈 구조이거나 하위 트리라고 하는 기본 유형이 t인 트리 구조의 유한 집합이 있는 유형 t의 노드입니다. 관련된.

다음으로 트리 구조로 작업할 때 사용되는 정의를 제공합니다.

노드 y가 노드 x 바로 아래에 있는 경우 노드 y는 노드 x의 직계 후손이라고 하며 x는 노드 y의 직계 조상입니다. 즉, 노드 x가 i 번째 수준에 있으면 노드 y는 그에 따라 (i + 1) - 번째 수준에 위치합니다.

트리 노드의 최대 레벨을 트리의 높이 또는 깊이라고 합니다. 조상에는 트리의 노드가 하나만 있는 것이 아닙니다. 바로 루트입니다.

자식이 없는 트리 노드를 리프 노드(또는 트리의 리프)라고 합니다. 다른 모든 노드를 내부 노드라고 합니다. 노드의 직계 자식 수는 해당 노드의 정도를 결정하고 주어진 트리에서 노드의 가능한 최대 차수는 트리의 정도를 결정합니다.

조상과 후손은 서로 바꿀 수 없습니다. 즉, 원본과 생성된 것 사이의 연결은 한 방향으로만 작용합니다.

트리의 루트에서 특정 노드로 이동하면 이 경우에 통과할 트리의 분기 수를 이 노드의 경로 길이라고 합니다. 트리의 모든 가지(노드)가 정렬되면 트리를 정렬이라고 합니다.

이진 트리는 트리 구조의 특별한 경우입니다. 이들은 각 자식이 왼쪽 및 오른쪽 하위 트리라고 하는 최대 두 개의 자식을 갖는 트리입니다. 따라서 이진 트리는 차수가 XNUMX인 트리 구조입니다.

이진 트리의 순서는 다음 규칙에 따라 결정됩니다. 각 노드에는 고유한 키 필드가 있으며 각 노드에 대해 키 값은 왼쪽 하위 트리의 모든 키보다 크고 오른쪽 하위 트리의 모든 키보다 작습니다.

차수가 XNUMX보다 큰 나무를 강한 가지라고 합니다.

2. 나무에 대한 작업

또한 이진 트리와 관련된 모든 작업을 고려할 것입니다.

I. 나무 건설

정렬된 트리를 구성하는 알고리즘을 제시합니다.

1. 트리가 비어 있으면 데이터가 트리의 루트로 전송됩니다. 트리가 비어 있지 않으면 트리의 순서가 위반되지 않는 방식으로 분기 중 하나가 내려갑니다. 결과적으로 새 노드는 트리의 다음 리프가 됩니다.

2. 이미 존재하는 트리에 노드를 추가하려면 위의 알고리즘을 사용할 수 있습니다.

3. 트리에서 노드를 삭제할 때 주의해야 합니다. 제거할 노드가 리프이거나 자식이 하나만 있는 경우 작업은 간단합니다. 삭제할 노드에 두 개의 자손이 있는 경우 해당 노드를 해당 위치에 넣을 수 있는 노드를 자손 중에서 찾아야 합니다. 이것은 트리를 주문해야 하기 때문에 필요합니다.

다음과 같이 할 수 있습니다. 제거할 노드를 왼쪽 하위 트리에서 가장 큰 키 값을 가진 노드 또는 오른쪽 하위 트리에서 가장 작은 키 값을 가진 노드로 교체한 다음 원하는 노드를 리프로 삭제합니다.

Ⅱ. 주어진 키 필드 값으로 노드 찾기

이 작업을 수행할 때 트리를 횡단해야 합니다. 접두사, 중위사, 접미사 등 다양한 트리 작성 형식을 고려해야 합니다.

문제가 발생합니다. 가장 편리하게 작업할 수 있도록 트리의 노드를 표현하는 방법은 무엇입니까? 배열을 사용하여 트리를 표현하는 것이 가능하며, 각 노드는 문자 유형 정보 필드와 두 개의 참조 유형 필드가 있는 결합된 유형 값으로 설명됩니다. 그러나 이것은 트리가 미리 결정되지 않은 많은 수의 노드를 가지고 있기 때문에 그리 편리하지 않습니다. 따라서 트리를 설명할 때는 동적 변수를 사용하는 것이 가장 좋습니다. 그런 다음 각 노드는 주어진 수의 정보 필드에 대한 설명을 포함하는 동일한 유형의 값으로 표시되며 해당 필드의 수는 트리의 차수와 같아야 합니다. nil로 자손의 부재를 정의하는 것은 논리적입니다. 그런 다음 Pascal에서 이진 트리에 대한 설명은 다음과 같을 수 있습니다.

유형 TreeLink = ^트리;

나무 = 기록;

정보 : <데이터 유형>;

왼쪽, 오른쪽 : TreeLink;

끝.

3. 작업 구현의 예

1. 최소 높이의 n개의 노드로 구성된 트리 또는 완벽하게 균형 잡힌 트리를 구성합니다(이러한 트리의 왼쪽 및 오른쪽 하위 트리의 노드 수는 XNUMX개 이하이어야 함).

재귀 생성 알고리즘:

1) 첫 번째 노드는 트리의 루트로 사용됩니다.

2) nl개 노드의 왼쪽 서브트리도 같은 방식으로 구축됩니다.

3) nr개 노드의 오른쪽 하위 트리가 같은 방식으로 구축됩니다.

nr = n - nl - 1. 정보 필드로 키보드에서 입력한 노드 번호를 가져옵니다. 이 구성을 구현하는 재귀 함수는 다음과 같습니다.

함수 트리(n : 바이트) : TreeLink;

변수: TreeLink; nl,nr,x : 바이트;

시작

n = 0이면 트리 := nil

다른

시작

nl := n div 2;

nr = n - nl - 1;

writeln('꼭짓점 번호를 입력하세요');

readln(x);

영원);

t^.inf := x;

t^.left := 나무(nl);

t^.right := 나무(nr);

나무 := t;

끝;

{나무}

끝.

2. 이진 순서 트리에서 키 필드의 주어진 값을 가진 노드를 찾습니다. 트리에 그러한 요소가 없으면 트리에 추가하십시오.

검색 절차(x : 바이트; var t : TreeLink);

시작

t = nil이면

시작

영원);

t^inf := x;

t^.left := nil;

t^.right := 없음;

종료

그렇지 않으면 x < t^.inf이면

검색(x, t^.left)

그렇지 않으면 x > t^.inf이면

검색(x, t^.right)

다른

시작

{프로세스 발견 요소}

...

끝;

끝.

3. 트리 순회 절차를 각각 정방향, 대칭 및 역순으로 작성합니다.

3.1. 절차 선주문(t : TreeLink);

시작

t <> nil이면

시작

쓰기(t^.inf);

선주문(t^.left);

선주문(t^.right);

끝;

끝;

3.2. 프로시저 Inorder(t : TreeLink);

시작

t <> nil이면

시작

중위(t^.left);

쓰기(t^.inf);

중위(t^.right);

끝;

끝.

3.3. 절차 Postorder(t : TreeLink);

시작

t <> nil이면

시작

포스트 오더(t^.left);

포스트 오더(t^.right);

쓰기(t^.inf);

끝;

끝.

4. 이진 순서 트리에서 키 필드의 주어진 값으로 노드를 삭제합니다.

트리에 필요한 요소의 존재와 이 노드의 자손 수를 고려하는 재귀적 절차를 설명하겠습니다. 삭제할 노드에 두 개의 자식이 있는 경우 왼쪽 하위 트리에서 가장 큰 키 값으로 대체되고 그 다음에야 영구적으로 삭제됩니다.

프로시저 Delete1(x : 바이트; var t : TreeLink);

변수 p : TreeLink;

프로시저 삭제2(var q : TreeLink);

시작

q^.right <> nil이면 Delete2(q^.right)

다른

시작

p^.inf := q^.inf;

피 := q;

q := q^.왼쪽;

끝;

끝;

시작

t = nil이면

Writeln('요소를 찾을 수 없음')

그렇지 않으면 x < t^.inf이면

삭제1(x, t^.left)

그렇지 않으면 x > t^.inf이면

삭제1(x, t^.right)

다른

시작

피 := t;

p^.left = nil이면

t := p^.오른쪽

다른

p^.right = nil이면

t := p^.왼쪽

다른

삭제2(p^.left);

끝;

끝.

강의 10. 개수

1. 그래프의 개념. 그래프를 나타내는 방법

그래프는 G = (V,E) 쌍입니다. 여기서 V는 정점이라고 하는 임의의 속성을 가진 개체 집합이고 E는 가장자리라고 하는 쌍 ei = (vil, vi2), vijoV 쌍의 집합입니다. 일반적으로 집합 V 및/또는 E 패밀리는 무한한 수의 요소를 포함할 수 있지만 유한 그래프, 즉 V와 E가 모두 유한인 그래프만 고려할 것입니다. ei에 포함된 요소의 순서가 중요하면 그래프를 방향성, 축약형 - 쌍곡선, 그렇지 않으면 무방향성이라고 합니다. 쌍곡선의 모서리를 호라고 합니다. 다음 내용에서는 지정 없이 사용되는 "그래프"라는 용어(유향 또는 무향)가 무향 그래프를 의미한다고 가정합니다.

전자 = , 정점 v와 u를 가장자리의 끝이라고 합니다. 여기서 우리는 모서리 e가 각 정점 v와 u에 인접(입사)한다고 말합니다. 꼭짓점 v 및 및 및 인접(사고)이라고도 합니다. 일반적인 경우 e = ; 이러한 모서리를 루프라고 합니다.

그래프에서 정점의 차수는 해당 정점에 입사하는 모서리의 수이며 루프는 두 번 계산됩니다. 각 모서리는 두 개의 꼭지점에 입사하므로 그래프의 모든 꼭지점 각도의 합은 모서리 수의 두 배와 같습니다. Sum(deg(vi), i=1...|V|) = 2 * | 전자|.

노드의 가중치는 주어진 노드에 할당된 숫자(실수, 정수 또는 유리수)입니다(비용, 처리량 등으로 해석됨). 가중치, 가장자리 길이 - 길이, 대역폭 등으로 해석되는 숫자 또는 여러 숫자입니다.

그래프의 경로(또는 유향 그래프의 경로)는 v0, (v0,v1), v1..., (vn - 1,vn) 형식의 정점과 모서리(또는 유향 그래프의 호)가 교대로 반복되는 순서입니다. ), vn. 숫자 n을 경로 길이라고 합니다. 반복되는 가장자리가 없는 경로를 체인이라고 하며, 반복되는 정점이 없는 경로를 단순 체인이라고 합니다. 경로를 닫을 수 있습니다(v0 = vn). 반복되는 가장자리가 없는 닫힌 경로를 순환(또는 이중 그래프의 윤곽선)이라고 합니다. 정점을 반복하지 않고(첫 번째와 마지막 제외) - 간단한 루프입니다.

그래프는 두 정점 사이에 경로가 있으면 연결되어 있고 그렇지 않으면 연결이 해제되어 있습니다. 연결이 끊긴 그래프는 연결된 여러 구성 요소(연결된 하위 그래프)로 구성됩니다.

그래프를 표현하는 방법은 다양합니다. 각각을 별도로 고려합시다.

1. 인시던트 매트릭스.

이것은 nxn 차원의 직사각형 행렬입니다. 여기서 n은 정점 수이고 am은 가장자리 수입니다. 행렬 요소의 값은 다음과 같이 결정됩니다. 모서리 xi와 정점 vj가 일치하면 해당 행렬 요소의 값은 1과 같고, 그렇지 않으면 값은 1입니다. 유향 그래프의 경우 입사 행렬은 다음 원리에 따라 구성됩니다. 모서리 xi가 꼭지점 vj에서 오면 요소 값은 -XNUMX과 같고, 모서리 xi가 꼭지점 vj에 들어가면 XNUMX과 같고, 그렇지 않으면 XNUMX과 같습니다. .

2. 인접 행렬.

이것은 nxn 차원의 정사각 행렬입니다. 여기서 n은 정점 수입니다. 정점 vi와 vj가 인접하면, 즉 정점을 연결하는 가장자리가 있으면 해당 행렬 요소는 1과 같고, 그렇지 않으면 0과 같습니다. 유향 그래프와 무향 그래프에 대해 이 행렬을 구성하는 규칙은 다르지 않습니다. 인접 행렬은 입사 행렬보다 더 컴팩트합니다. 이 행렬도 매우 희소한 행렬이지만 무방향 그래프의 경우 주대각선을 기준으로 대칭이므로 전체 행렬이 아니라 절반만 저장할 수 있습니다(삼각 행렬). ).

3. 인접 항목(사건) 목록.

각 그래프 정점에 대한 인접 정점 목록을 저장하는 데이터 구조입니다. 목록은 포인터의 배열이며, i 번째 요소에는 i 번째 정점에 인접한 정점 목록에 대한 포인터가 포함되어 있습니다.

인접 목록은 null 요소의 저장을 제거하기 때문에 인접 행렬보다 더 효율적입니다.

4. 목록 목록.

이것은 하나의 분기가 각 그래프 정점에 인접한 정점 목록을 포함하고 두 번째 분기가 다음 그래프 정점을 가리키는 트리와 같은 데이터 구조입니다. 그래프를 나타내는 이 방법이 가장 최적입니다.

2. 발생 목록에 의한 그래프 표현. 그래프 깊이 순회 알고리즘

그래프를 발생 목록으로 구현하려면 다음 유형을 사용할 수 있습니다.

유형 목록 = ^S;

S=기록;

inf : 바이트;

다음 : 목록;

끝;

그러면 그래프는 다음과 같이 정의됩니다.

Var Gr : 목록의 배열[1..n];

이제 그래프 순회 절차를 살펴보겠습니다. 이것은 그래프의 모든 정점을 보고 모든 정보 필드를 분석할 수 있는 보조 알고리즘입니다. 그래프 순회를 심층적으로 고려하면 재귀 및 비재귀의 두 가지 유형의 알고리즘이 있습니다.

재귀적 깊이 우선 탐색 알고리즘을 사용하여 임의의 정점을 선택하고 이에 인접한 임의의 보이지 않는(새로운) 정점 v를 찾습니다. 그런 다음 정점 v를 새로운 것이 아닌 것으로 간주하고 그것에 인접한 새로운 정점을 찾습니다. 일부 정점에 보이지 않는 새로운 정점이 없으면 이 정점이 사용된다고 가정하고 사용된 정점에 도달한 정점보다 한 단계 높은 수준으로 반환합니다. 탐색은 그래프에 새로운 스캔되지 않은 정점이 없을 때까지 이러한 방식으로 계속됩니다.

Pascal에서 깊이 우선 탐색 절차는 다음과 같습니다.

절차 Obhod(gr : 그래프; k : 바이트);

변수 g : 그래프; l : 목록;

시작

nov[k] := 거짓;

g := gr;

동안 g^.inf <> k

g := g^.다음;

내가 := g^.smeg;

l <> nil 시작하는 동안

nov[l^.inf]이면 Obhod(gr, l^.inf);

l := l^.다음;

끝;

끝;

주의

이 절차에서는 그래프 유형을 설명할 때 목록의 목록으로 그래프를 설명하는 것을 의미했습니다. 배열 nov[i]는 i번째 정점을 방문하지 않으면 i번째 요소가 True이고 그렇지 않으면 False인 특수 배열입니다.

비재귀 탐색 알고리즘도 자주 사용됩니다. 이 경우 재귀는 스택으로 대체됩니다. 정점을 본 후에는 스택으로 푸시되고 인접한 새로운 정점이 더 이상 없을 때 사용됩니다.

3. 목록 목록에 의한 그래프 표현. 너비 그래프 순회 알고리즘

그래프는 다음과 같이 목록 목록을 사용하여 정의할 수 있습니다.

유형 목록 = ^Tlist;

tlist=기록

inf : 바이트;

다음 : 목록;

끝;

그래프 = ^TGpaph;

TGpaph = 기록

inf : 바이트;

smeg : 목록;

다음 : 그래프;

끝;

폭으로 그래프를 탐색할 때 임의의 정점을 선택하고 인접한 모든 정점을 한 번에 살펴봅니다. 스택 대신 큐가 사용됩니다. 너비 우선 탐색 알고리즘은 그래프에서 최단 경로를 찾는 데 매우 유용합니다.

다음은 의사 코드에서 너비의 그래프를 탐색하는 절차입니다.

절차 Obhod2(v);

{값 spisok, XNUMX월 - 전역}

시작

대기열 = O;

큐 <= v;

nov[v] = 거짓;

동안 큐 <> O 할

시작

p <= 큐;

당신을 위해 spisok(p)

새[u]인 경우

시작

nov[u] := 거짓;

큐 <= 유;

끝;

끝;

끝;

강의 #11. 객체 데이터 유형

1. 파스칼의 객체 유형. 대상의 개념, 설명 및 사용

역사적으로 프로그래밍에 대한 첫 번째 접근 방식은 상향식 프로그래밍으로 알려진 절차적 프로그래밍이었습니다. 처음에는 다양한 컴퓨터 응용 분야에서 사용되는 표준 프로그램의 공통 라이브러리가 만들어졌습니다. 그런 다음 이러한 프로그램을 기반으로 특정 문제를 해결하기 위해 더 복잡한 프로그램을 만들었습니다.

그러나 컴퓨터 기술은 지속적으로 발전하고 생산, 경제의 다양한 문제를 해결하는 데 사용되기 시작했으며 따라서 다양한 형식의 데이터를 처리하고 비표준 문제(예: 비숫자 문제)를 해결하는 것이 필요하게 되었습니다. 따라서 프로그래밍 언어를 개발할 때 다양한 유형의 데이터 생성에주의를 기울이기 시작했습니다. 이것은 결합, 다중, 문자열, 파일 등과 같은 복잡한 데이터 유형의 출현에 기여했습니다. 문제를 해결하기 전에 프로그래머는 분해를 수행했습니다. . 주요 프로그래밍 기술에는 세 단계가 포함됩니다.

1) 하향식 디자인;

2) 모듈식 프로그래밍;

3) 구조적 코딩.

그러나 XX 세기의 60 년대 중반부터 새로운 개념과 접근 방식이 형성되기 시작하여 객체 지향 프로그래밍 기술의 기초가 형성되었습니다. 이 접근법에서는 실제 세계의 모델링 및 설명이 해결되는 문제가 속하는 특정 주제 영역의 개념 수준에서 수행됩니다.

객체 지향 프로그래밍은 우리의 행동과 매우 유사한 프로그래밍 기술입니다. 이는 프로그래밍 언어 디자인의 초기 혁신이 자연스럽게 진화한 것입니다. 객체 지향 프로그래밍은 구조적 프로그래밍에 관한 이전의 모든 개발보다 더 구조적입니다. 또한 내부적으로 데이터 추상화 및 프로그래밍 세부 사항에 대한 이전 시도보다 더 모듈화되고 더 추상적입니다. 객체 지향 프로그래밍 언어는 세 가지 주요 속성이 특징입니다.

1) 캡슐화. 이러한 레코드의 필드를 조작하는 프로시저 및 기능과 레코드를 결합하면 새로운 데이터 유형인 객체가 형성됩니다.

2) 상속. 개체의 정의 및 계층 구조와 관련된 각 하위 개체가 모든 상위 개체의 코드 및 데이터에 액세스할 수 있는 기능을 사용하여 하위 개체의 계층 구조를 구축하는 데 사용합니다.

3) 다형성. 작업에 단일 이름을 부여한 다음 개체 계층의 위아래로 공유되며 계층의 각 개체가 적절한 방식으로 해당 작업을 수행합니다.

객체에 대해 말하자면, 우리는 새로운 데이터 유형인 객체를 소개합니다. 개체 유형은 고정된 수의 구성 요소로 구성된 구조입니다. 각 구성 요소는 엄격하게 정의된 유형의 데이터를 포함하는 필드이거나 개체에 대해 작업을 수행하는 메서드입니다. 변수 선언과 유추하여 필드 선언은 이 필드의 데이터 유형과 필드를 명명하는 식별자를 나타냅니다. 프로시저 또는 함수 선언과 유추하여 메서드 설명은 프로시저 제목을 나타냅니다. 함수, 생성자 또는 소멸자.

개체 유형은 다른 개체 유형의 구성 요소를 상속할 수 있습니다. T2 형식이 T1 형식에서 상속되는 경우 T2 형식은 T1 형식의 자식이고 T1 형식 자체는 T2 형식의 부모입니다. 상속은 전이적입니다. 즉, TK가 T2에서 상속하고 T2가 T1에서 상속하는 경우 TK는 T1에서 상속합니다. 개체 유형의 범위(도메인)는 자신과 모든 하위 항목으로 구성됩니다.

다음 소스 코드는 객체 유형 선언의 예입니다.

유형

점 = 물체

X, Y: 정수;

끝;

사각형 = 객체

A, B: T포인트;

절차 Init(XA, YA, XB, YB: 정수);

절차 복사(var R: TRectangle);

절차 이동(DX, DY: 정수);

절차 Grow(DX, DY: 정수);

프로시저 Intersect(var R: TRectangle);

절차 Union(var R: TRectangle);

함수 포함(P: 포인트): 부울;

끝;

StringPtr = ^문자열;

FieldPtr = ^T필드;

TField = 객체

X, Y, Len: 정수;

이름: StringPtr;

생성자 복사(var F: TField);

생성자 초기화(FX, FY, FLen: 정수, FName: 문자열);

소멸자 완료; 가상;

절차 표시; 가상;

절차 편집; 가상;

함수 GetStr: 문자열; 가상;

함수 PutStr(S: 문자열): 부울; 가상;

끝;

StrFieldPtr = ^TStr필드;

StrField = 개체(TField)

값: PString;

생성자 초기화(FX, FY, FLen: 정수, FName: 문자열);

소멸자 완료; 가상;

함수 GetStr: 문자열; 가상;

함수 PutStr(S: 문자열): 부울;

가상;

함수 가져오기:문자열;

프로시저 Put(S: 문자열);

끝;

NumFieldPtr = ^TNumField;

TNumField = 개체(TField)

사설

값, 최소값, 최대값: Longint;

공개

생성자 초기화(FX, FY, FLen: 정수, FName: 문자열,

FMin, FMax: Longint);

함수 GetStr: 문자열; 가상;

함수 PutStr(S: 문자열): 부울; 가상;

함수 가져오기: Longint;

함수 Put(N: Longint);

끝;

ZipFieldPtr = ^TZipField;

ZipField = 개체(TNumField)

함수 GetStr: 문자열; 가상;

함수 PutStr(S: 문자열): 부울;

가상;

끝.

다른 유형과 달리 개체 유형은 프로그램 또는 모듈 범위의 가장 바깥쪽 수준에 있는 유형 선언 섹션에서만 선언할 수 있습니다. 따라서 객체 유형은 변수 선언 섹션이나 프로시저, 함수 또는 메서드 블록 내부에서 선언할 수 없습니다.

파일 유형 구성 요소 유형은 객체 유형 또는 객체 유형 구성 요소를 포함하는 구조 유형을 가질 수 없습니다.

2. 상속

한 유형이 다른 유형의 특성을 상속하는 프로세스를 상속이라고 합니다. 자손을 파생(자식)형이라고 하고, 자식형이 상속받은 유형을 부모(부모)형이라고 합니다.

이전에 알려진 Pascal 레코드 유형은 상속할 수 없습니다. 그러나 Borland Pascal은 상속을 지원하기 위해 Pascal 언어를 확장합니다. 이러한 확장 중 하나는 레코드와 관련된 새로운 데이터 구조 범주이지만 훨씬 더 강력합니다. 이 새 범주의 데이터 유형은 새 예약어 "object"를 사용하여 정의됩니다. 객체 유형은 파스칼 항목을 설명하는 방식으로 완전하고 독립적인 유형으로 정의할 수 있지만 예약어 "객체" 뒤에 괄호 안에 부모 유형을 넣어 기존 객체 유형의 자손으로 정의할 수도 있습니다.

3. 객체 인스턴스화

개체 인스턴스는 개체 유형의 변수 또는 상수를 선언하거나 "개체 유형에 대한 포인터" 유형의 변수에 표준 절차 New를 적용하여 생성됩니다. 결과 개체를 개체 유형의 인스턴스라고 합니다.

였다

F: T필드;

지: TZipField;

FP: P필드;

ZP: PZip필드;

이러한 변수 선언이 주어지면 F는 TField의 인스턴스이고 Z는 TZipField의 인스턴스입니다. 마찬가지로 FP 및 ZP에 New를 적용한 후 FP는 TField 인스턴스를 가리키고 ZP는 TZipField 인스턴스를 가리킵니다.

객체 유형에 가상 메서드가 포함된 경우 해당 객체 유형의 인스턴스는 가상 메서드를 호출하기 전에 생성자를 호출하여 초기화해야 합니다.

다음은 예입니다.

였다

S: StrField;

S.Init(1, 1, 25, '이름');

S.Put('블라디미르');

S.디스플레이;

...

S 완료;

끝.

S.Init가 호출되지 않은 경우 S.Display를 호출하면 이 예제가 실패합니다.

개체 유형의 인스턴스를 할당한다고 해서 인스턴스가 초기화되는 것은 아닙니다. 개체는 생성자의 호출과 실행이 실제로 생성자의 코드 블록에서 첫 번째 문에 도달하는 지점 사이에서 실행되는 컴파일러 생성 코드에 의해 초기화됩니다.

개체 인스턴스가 초기화되지 않고 범위 검사가 활성화된 경우({SR+} 지시문에 의해) 개체 인스턴스의 가상 메서드에 대한 첫 번째 호출은 런타임 오류를 제공합니다. 범위 검사가 비활성화된 경우({SR-} 지시문에 의해) 초기화되지 않은 개체의 가상 메서드에 대한 첫 번째 호출은 예측할 수 없는 동작을 초래할 수 있습니다.

필수 초기화 규칙은 구조체 유형의 구성 요소인 인스턴스에도 적용됩니다. 예를 들어:

였다

주석: TStrField의 배열 [1..5];

나: 정수

시작하다

나는 := 1에서 5까지

주석 [I].Init (1, I + 10, 40, '이름');

.

.

.

for I := 1 to 5 코멘트 [I].Done;

끝;

동적 인스턴스의 경우 일반적으로 초기화는 배치이고 정리는 폐기이며, 이는 New 및 Dispose 표준 절차의 확장된 구문을 통해 수행됩니다. 예를 들어:

였다

SP: StrFieldPtr;

시작하다

New(SP, Init(1, 1, 25, '이름');

SP^.Put('블라디미르');

SP^.디스플레이;

.

.

.

폐기(SP, 완료);

끝.

개체 유형에 대한 포인터는 모든 상위 개체 유형에 대한 포인터와 할당이 호환되므로 런타임에 개체 유형에 대한 포인터는 해당 유형의 인스턴스 또는 모든 하위 유형의 인스턴스를 가리킬 수 있습니다.

예를 들어 ZipFieldPtr 유형의 포인터는 PZipField, PNumField 및 PField 유형의 포인터에 할당될 수 있으며 런타임에 PField 유형의 포인터는 nil이거나 TField, TNumField 또는 TZipField의 인스턴스를 가리킬 수 있습니다. TField의 자식 유형의 인스턴스.

이러한 할당 포인터 호환성 규칙은 개체 유형 매개변수에도 적용됩니다. 예를 들어, TField.Cop 메소드는 TField, TStrField, TNumField, TZipField 또는 TField의 다른 유형 하위 유형의 인스턴스를 전달할 수 있습니다.

4. 구성 및 범위

빈 식별자의 범위는 객체 유형을 넘어 확장됩니다. 게다가, 빈 식별자의 범위는 객체 유형과 그 자손의 메소드를 구현하는 프로시저, 함수, 생성자 및 소멸자의 블록을 통해 확장됩니다. 이러한 고려 사항을 기반으로 구성 요소 식별자의 철자는 개체 유형 내에서, 모든 하위 항목 및 모든 메서드 내에서 고유해야 합니다.

형식 선언의 private 부분에 설명된 구성 요소 식별자의 범위는 개체 형식 선언을 포함하는 모듈(프로그램)로 제한됩니다. 즉, 개인 식별자 빈은 객체 유형 선언을 포함하는 모듈 내에서 일반 공용 식별자처럼 작동하고 모듈 외부에서 모든 개인 빈 및 식별자는 알 수 없고 액세스할 수 없습니다. 관련 객체 유형을 동일한 모듈에 넣으면 이러한 객체가 서로의 개인 구성 요소에 액세스하도록 할 수 있으며 이러한 개인 구성 요소는 다른 모듈에 알려지지 않습니다.

객체 유형 선언에서 메소드 헤더는 선언이 아직 완료되지 않은 경우에도 설명되는 객체 유형의 매개변수를 지정할 수 있습니다.

강의 12. 방법

1. 방법

개체 유형 내부의 메서드 선언은 정방향 메서드 선언(forward)에 해당합니다. 따라서 개체 유형 선언 후 어딘가에 개체 유형 선언의 범위와 동일한 범위 내에서 해당 선언을 정의하여 메서드를 구현해야 합니다.

절차적 및 기능적 메서드의 경우 정의 선언은 일반 프로시저 또는 함수 선언의 형식을 취하지만 이 경우 프로시저 또는 함수 식별자가 메서드 식별자로 취급된다는 점은 예외입니다.

생성자 및 소멸자 메서드의 경우 정의 선언은 프로시저 메서드 선언의 형식을 취합니다. 단, 예약어 프로시저가 예약어 생성자 또는 소멸자로 대체된다는 점은 예외입니다.

정의하는 메서드 선언은 객체 유형에서 메서드 헤더의 형식 매개변수 목록을 반복할 수 있지만 반드시 그럴 필요는 없습니다. 이 경우 메소드 헤더는 객체 유형의 헤더와 순서, 유형, 매개변수 이름과 정확히 일치해야 하며 메소드가 함수인 경우 함수 결과의 반환 유형과 정확히 일치해야 합니다.

메서드의 정의 설명에는 항상 개체 유형의 형식 변수 매개 변수에 해당하는 식별자 Self가 있는 암시적 매개 변수가 포함됩니다. 메서드 블록 내에서 Self는 메서드를 호출하기 위해 메서드 구성 요소가 지정된 인스턴스를 나타냅니다. 따라서 Self 필드의 값에 대한 모든 변경 사항은 인스턴스에 반영됩니다.

객체 유형 빈 식별자의 범위는 해당 객체 유형의 메소드를 구현하는 프로시저, 함수, 생성자 및 소멸자의 블록으로 확장됩니다. 그 효과는 메소드 블록의 시작 부분에 다음 형식의 with 문이 삽입된 것과 같습니다.

셀프로

시작하다

...

끝;

이러한 고려 사항에 따라 구성 요소 식별자의 철자, 형식 메서드 매개 변수, 자체 및 메서드의 실행 가능한 부분에 도입된 식별자는 고유해야 합니다.

고유한 메서드 식별자가 필요한 경우 정규화된 메서드 식별자가 사용됩니다. 이것은 객체 유형 식별자 뒤에 점과 메서드 식별자로 구성됩니다. 다른 식별자와 마찬가지로 정규화된 메서드 식별자 앞에 패키지 식별자와 마침표가 선택적으로 올 수 있습니다.

가상 방법

메서드는 기본적으로 정적이지만 생성자를 제외하고 가상일 수 있습니다(메서드 선언에 가상 지시문을 포함하여). 컴파일러는 컴파일 프로세스 중에 정적 메서드 호출에 대한 참조를 확인하는 반면 가상 메서드에 대한 호출은 런타임에 확인됩니다. 이것을 후기 바인딩이라고도 합니다.

객체 유형이 가상 메소드를 선언하거나 상속하는 경우 해당 유형의 변수는 가상 메소드를 호출하기 전에 생성자를 호출하여 초기화해야 합니다. 따라서 가상 메서드를 설명하거나 상속하는 개체 형식은 하나 이상의 생성자 메서드도 설명하거나 상속해야 합니다.

개체 유형은 부모로부터 상속받은 모든 메서드를 재정의할 수 있습니다. 자식의 메서드 선언이 부모의 메서드 선언과 동일한 메서드 식별자를 지정하면 자식의 선언이 부모의 선언을 재정의합니다. 재정의하는 메서드의 범위는 메서드가 도입된 자식의 범위로 확장되며 메서드 식별자가 다시 재정의될 때까지 유지됩니다.

정적 메서드 재정의는 메서드 헤더 변경과 무관합니다. 대조적으로 가상 메서드 재정의는 순서, 매개변수 유형 및 이름, 함수 결과 유형(있는 경우)을 유지해야 합니다. 또한 재정의에는 가상 지시문이 다시 포함되어야 합니다.

동적 방법

Borland Pascal은 동적 메서드라고 하는 추가 후기 바인딩 메서드를 지원합니다. 동적 메서드는 런타임에 전달되는 방식에서만 가상 메서드와 다릅니다. 다른 모든 측면에서 동적 메서드는 가상 메서드와 동일한 것으로 간주됩니다.

동적 메서드 선언은 가상 메서드 선언과 동일하지만 동적 메서드 선언에는 virtual 키워드 바로 뒤에 지정되는 동적 메서드 인덱스가 포함되어야 합니다. 동적 메서드의 인덱스는 1에서 656535 사이의 정수 상수여야 하며 개체 유형 또는 해당 상위 항목에 포함된 다른 동적 메서드의 인덱스 간에 고유해야 합니다. 예를 들어:

프로시저 FileOpen(var Msg: TMessage); 가상 100;

동적 메소드의 재정의는 매개변수의 순서, 유형 및 이름과 일치해야 하고 상위 메소드 함수의 결과 유형과 정확히 일치해야 합니다. 재정의에는 상위 개체 유형에 지정된 것과 동일한 동적 메서드 인덱스가 뒤에 오는 가상 지시문도 포함되어야 합니다.

2. 생성자와 소멸자

생성자와 소멸자는 메서드의 특수한 형태입니다. New 및 Dispose 표준 프로시저의 확장된 구문과 관련하여 사용되는 생성자와 소멸자는 동적 개체를 배치하고 제거할 수 있습니다. 또한 생성자는 가상 메서드가 포함된 개체의 필수 초기화를 수행할 수 있습니다. 모든 메서드와 마찬가지로 생성자와 소멸자는 상속될 수 있으며 개체에는 원하는 수의 생성자와 소멸자가 포함될 수 있습니다.

생성자는 새로 생성된 객체를 초기화하는 데 사용됩니다. 일반적으로 초기화는 생성자에 매개변수로 전달된 값을 기반으로 합니다. 가상 메서드의 디스패치 메커니즘은 객체를 먼저 초기화한 생성자에 의존하기 때문에 생성자는 가상이 될 수 없습니다.

다음은 생성자의 몇 가지 예입니다.

생성자 Field.Copy(var F: 필드);

시작하다

본인 := F;

끝;

생성자 Field.Init(FX, FY, FLen: 정수, FName: 문자열);

시작하다

X := FX;

Y := 회계연도;

GetMem(이름, 길이(F이름) + 1);

이름^ := F이름;

끝;

생성자 TStrField.Init(FX, FY, FLen: 정수, FName: 문자열);

시작하다

상속된 초기화(FX, FY, FLen, FName);

Field.Init(FX, FY, FLen, FName);

GetMem(값, 렌);

값^ := '';

끝;

위의 TStr 필드와 같은 파생(자식) 유형의 생성자의 주요 작업입니다. Init는 거의 항상 직계 부모의 적절한 생성자를 호출하여 개체의 상속된 필드를 초기화합니다. 이 절차를 수행한 후 생성자는 파생된 형식에만 속하는 개체의 필드를 초기화합니다.

소멸자는 생성자의 반대이며 사용된 객체를 정리하는 데 사용됩니다. 일반적으로 정리는 개체의 모든 포인터 필드를 제거하는 것으로 구성됩니다.

주의

소멸자는 가상일 수 있으며 종종 그렇습니다. 소멸자에는 매개변수가 거의 없습니다.

다음은 소멸자의 몇 가지 예입니다.

소멸자 필드 완료;

시작하다

FreeMem(이름, 길이(이름^) + 1);

끝;

소멸자 StrField.Done;

시작하다

FreeMem(값, 렌);

필드 완료;

끝;

위의 TStrField와 같은 자식 유형의 소멸자입니다. 완료, 일반적으로 먼저 파생된 형식에 도입된 포인터 필드를 제거한 다음 마지막 단계로 직계 부모의 적절한 수집기 소멸자를 호출하여 개체의 상속된 포인터 필드를 제거합니다.

3. 소멸자

Borland Pascal은 동적으로 할당된 객체를 정리하고 삭제하기 위해 가비지 수집기(또는 소멸자)라고 하는 특별한 유형의 메서드를 제공합니다. 소멸자는 개체를 삭제하는 단계를 해당 개체 유형에 필요한 다른 작업이나 작업과 결합합니다. 단일 객체 유형에 대해 여러 소멸자를 정의할 수 있습니다.

소멸자는 객체의 유형 정의에서 다른 모든 객체 메소드와 함께 정의됩니다.

유형

직원 = 개체

이름: 문자열[25];

제목: 문자열[25];

비율: 실제;

생성자 초기화(AName, ATitle: 문자열, ARate: Real);

소멸자 완료; 가상;

함수 GetName: 문자열;

함수 GetTitle: 문자열;

함수 GetRate: 비율; 가상;

함수 GetPayAmount: 실제; 가상;

끝;

소멸자는 상속될 수 있으며 정적 또는 가상일 수 있습니다. 다른 종료자는 다른 유형의 객체를 요구하는 경향이 있기 때문에 일반적으로 각 객체 유형에 대해 올바른 소멸자가 실행되도록 소멸자를 항상 가상 상태로 유지하는 것이 좋습니다.

객체의 유형 정의에 가상 메소드가 포함된 경우에도 모든 정리 메소드에 예약어 소멸자를 지정할 필요는 없습니다. 소멸자는 실제로 동적으로 할당된 개체에서만 작동합니다.

동적으로 할당된 객체가 정리될 때 소멸자는 특별한 기능을 수행합니다. 즉, 동적으로 할당된 메모리 영역에서 항상 올바른 수의 바이트가 해제되도록 합니다. 정적으로 할당된 개체와 함께 소멸자를 사용하는 것에 대해 걱정할 필요가 없습니다. 사실, 객체의 유형을 소멸자에 전달하지 않음으로써 프로그래머는 해당 유형의 객체에서 Borland Pascal의 동적 메모리 관리의 모든 이점을 박탈합니다.

소멸자는 실제로 다형성 개체를 지워야 하고 해당 개체가 차지하는 메모리를 할당 해제해야 할 때 자신이 됩니다.

다형성 객체는 볼랜드 파스칼의 확장된 유형 호환성 규칙으로 인해 부모 유형에 할당된 객체입니다. TEmployee 유형의 변수에 할당된 Tourly 유형의 개체 인스턴스는 다형성 개체의 예입니다. 이러한 규칙은 개체에도 적용할 수 있습니다. Tourly에 대한 포인터는 TEmployee에 대한 포인터에 자유롭게 할당할 수 있으며 해당 포인터가 가리키는 개체는 다시 다형성 개체가 됩니다. "다형성"이라는 용어는 컴파일 타임에 개체를 처리하는 코드가 최종적으로 처리해야 하는 개체 유형을 정확히 "모르는" 것이기 때문에 적절합니다. 이 개체가 지정된 개체 유형의 하위 항목인 개체 계층에 속한다는 사실만 알고 있습니다.

분명히 개체 유형의 크기는 다릅니다. 따라서 힙 할당 다형성 개체를 정리할 때 Dispose가 해제할 힙 공간의 바이트 수를 어떻게 알 수 있습니까? 컴파일 시간에 다형성 개체에서 개체 크기에 대한 정보를 추출할 수 없습니다.

소멸자는 TCM 구현 변수에서 이 정보가 기록된 위치를 참조하여 이 퍼즐을 해결합니다. 개체 유형의 각 TBM에는 해당 개체 유형의 크기(바이트)가 포함됩니다. 모든 개체의 가상 메서드 테이블은 메서드가 호출될 때 메서드로 전송되는 숨겨진 매개 변수 Self를 통해 사용할 수 있습니다. 소멸자는 메서드의 한 유형일 뿐이므로 객체가 호출하면 소멸자는 스택에서 Self의 복사본을 가져옵니다. 따라서 객체가 컴파일 시간에 다형성이면 런타임에 바인딩이 지연되기 때문에 런타임에 다형성이 되지 않습니다.

이 늦은 바인딩 해제를 수행하려면 소멸자를 Dispose 프로시저의 확장 구문의 일부로 호출해야 합니다.

폐기(P, 완료);

(Dispose 프로시저 외부에서 소멸자를 호출해도 메모리 할당이 전혀 해제되지 않습니다.) 여기서 실제로 발생하는 것은 P가 가리키는 개체의 가비지 수집기가 일반 메서드처럼 실행된다는 것입니다. 그러나 마지막 작업이 완료되면 소멸자는 TCM에서 해당 유형의 구현 크기를 조회하고 크기를 Dispose 프로시저에 전달합니다. Dispose 프로시저는 이전에 P^에 속한 힙 공간(공간)의 올바른 바이트 수를 삭제하여 프로세스를 종료합니다. 해제될 바이트 수는 P가 TSalaried 유형의 인스턴스를 가리키는지 여부 또는 TCommissioned와 같은 TSalaried 유형의 자식 유형 중 하나를 가리켰는지 여부에 관계없이 정확합니다.

소멸자 메서드 자체는 비어 있을 수 있으며 다음 기능만 수행할 수 있습니다.

소멸자AnObject.Done;

시작하다

끝;

이 소멸자에서 유용한 것은 본체의 속성이 아니지만 컴파일러는 소멸자 예약어에 대한 응답으로 에필로그 코드를 생성합니다. 아무것도 내보내지 않는 모듈과 같지만 프로그램을 시작하기 전에 초기화 섹션을 실행하여 보이지 않는 작업을 수행합니다. 모든 작업은 무대 뒤에서 이루어집니다.

4. 가상 방법

메서드는 객체 유형 선언 다음에 새로운 예약어 virtual이 오면 가상이 됩니다. 부모 형식의 메서드가 가상으로 선언된 경우 자식 형식에서 같은 이름을 가진 모든 메서드도 가상으로 선언해야 컴파일러 오류를 방지할 수 있습니다.

다음은 제대로 가상화된 급여 예의 개체입니다.

유형

PEmployee = ^TEmployee;

직원 = 개체

이름, 제목: 문자열[25];

비율: 실제;

생성자 초기화(AName, ATitle: 문자열, ARate: Real);

함수 GetPayAmount : 실수; 가상;

함수 GetName : 문자열;

함수 GetTitle : 문자열;

함수 GetRate : 실제;

절차 쇼; 가상;

끝;

매시간 = ^시간마다;

Tourly = 개체(TEmployee);

시간: 정수;

생성자 Init(AName, ATitle: 문자열; ARate: Real; 시간: 정수);

함수 GetPayAmount : 실수; 가상;

함수 GetTime : 정수;

끝;

PSalaried = ^TSSalared;

TSalared = 개체(TEmployee);

함수 GetPayAmount : 실수; 가상;

끝;

P커미션 = ^T 커미션됨;

TCommissioned = 개체(급여);

커미션 : 실제;

판매 금액 : 실제;

생성자 초기화(AName, ATitle: 문자열; ARate,

ACommission, ASalesAmount: Real);

함수 GetPayAmount : 실수; 가상;

끝;

생성자는 가상 메서드 메커니즘에 대한 일부 설정 작업을 수행하는 특수한 유형의 프로시저입니다. 또한 가상 메서드가 호출되기 전에 생성자를 호출해야 합니다. 생성자를 먼저 호출하지 않고 가상 메서드를 호출하면 시스템이 차단될 수 있으며 컴파일러는 메서드가 호출되는 순서를 확인할 방법이 없습니다.

가상 메서드가 있는 모든 개체 유형에는 생성자가 있어야 합니다.

경고

생성자는 다른 가상 메서드가 호출되기 전에 호출되어야 합니다. 생성자에 대한 이전 호출 없이 가상 메서드를 호출하면 시스템 잠금이 발생할 수 있으며 컴파일러는 메서드가 호출되는 순서를 확인할 수 없습니다.

주의

개체 생성자의 경우 식별자 Init를 사용하는 것이 좋습니다.

각 고유한 개체 인스턴스는 별도의 생성자 호출로 초기화해야 합니다. 개체의 한 인스턴스를 초기화한 다음 해당 인스턴스를 다른 인스턴스에 할당하는 것만으로는 충분하지 않습니다. 다른 인스턴스는 유효한 데이터를 포함하더라도 할당 연산자로 초기화되지 않으며 가상 메서드에 대한 모든 호출에서 시스템을 차단합니다. 예를 들어:

였다

FBee, GBee: 꿀벌; { 두 개의 Bee 인스턴스 생성 }

시작하다

FBee.Init(5, 9) { FBee에 대한 생성자 호출 }

GBee := FBee; { Gbee가 잘못되었습니다! }

끝;

생성자는 정확히 무엇을 생성합니까? 각 개체 유형에는 데이터 세그먼트에 VMT(가상 메서드 테이블)라는 항목이 포함되어 있습니다. TVM에는 개체 유형의 크기와 각 가상 메서드에 대해 해당 메서드를 실행하는 코드에 대한 포인터가 포함됩니다. 생성자는 객체의 호출 구현과 객체의 TCM 유형 간의 관계를 설정합니다.

각 개체 유형에 대해 TBM이 하나만 있음을 기억하는 것이 중요합니다. 개체 유형의 개별 인스턴스(즉, 이 유형의 변수)는 TBM에 대한 연결만 포함하지만 TBM 자체는 포함하지 않습니다. 생성자는 이 연결의 값을 TBM으로 설정합니다. 이 때문에 생성자를 호출하기 전에는 아무데도 실행을 시작할 수 없습니다.

5. 개체 데이터 필드 및 형식 메서드 매개변수

메서드와 해당 개체가 공통 범위를 공유한다는 사실의 의미는 메서드의 형식 매개 변수가 개체의 데이터 필드와 동일할 수 없다는 것입니다. 이것은 객체 지향 프로그래밍에 의해 부과된 몇 가지 새로운 제한 사항이 아니라 Pascal이 항상 가지고 있던 동일한 이전 범위 규칙입니다. 이것은 프로시저의 형식 매개변수가 프로시저의 지역 변수와 동일하지 않도록 하는 것과 같습니다.

절차 CrunchIt(Crunchee: MyDataRec, Crunchby,

오류 코드: 정수);

였다

A, B: 문자;

오류 코드: 정수;

시작하다

.

.

.

프로시저의 지역 변수와 형식 매개변수는 공통 범위를 공유하므로 동일할 수 없습니다. 이와 같은 것을 컴파일하려고 하면 "오류 4: 중복 식별자" 메시지가 표시됩니다. 형식 메서드 매개변수를 이 메서드가 속한 개체의 필드 이름으로 설정하려고 하면 동일한 오류가 발생합니다.

데이터 구조 내부에 프로시저 헤더를 넣는 것은 Turbo Pascal의 혁신에 대한 경의이지만 Pascal 범위의 기본 원칙은 변경되지 않았기 때문에 상황은 다소 다릅니다.

강의 13. 객체 유형의 호환성

1. 캡슐화

객체의 코드와 데이터의 조합을 캡슐화라고 합니다. 원칙적으로 개체의 사용자가 개체의 필드에 직접 액세스하지 않도록 충분한 메서드를 제공하는 것이 가능합니다. Smalltalk와 같은 일부 다른 객체 지향 언어는 필수 캡슐화를 요구하지만 Borland Pascal에는 선택권이 있습니다.

예를 들어, TEmployee 및 THourly 개체는 내부 데이터 필드에 직접 액세스할 필요가 전혀 없는 방식으로 작성되었습니다.

유형

직원 = 개체

이름, 제목: 문자열[25];

비율: 실제;

프로시저 Init(AName, ATitle: 문자열, ARate: Real);

함수 GetName : 문자열;

함수 GetTitle : 문자열;

함수 GetRate : 실제;

함수 GetPayAmount : 실수;

끝;

Tourly = 개체(TEmployee)

시간: 정수;

프로시저 초기화(AName, ATitle: 문자열; ARate:

실수, Atime: 정수);

함수 GetPayAmount : 실수;

끝;

여기에는 Name, Title, Rate 및 Time의 네 가지 데이터 필드만 있습니다. GetName 및 GetTitle 메서드는 각각 작업자의 성 및 위치를 표시합니다. GetPayAmount 메서드는 Rate를 사용하고, 근무하는 경우 THourly 및 Time을 사용하여 근무에 대한 지불 금액을 계산합니다. 더 이상 이러한 데이터 필드를 직접 참조할 필요가 없습니다.

THourly 유형의 AnHourly 인스턴스가 있다고 가정하면 다음과 같이 AnHourly 데이터 필드를 조작하는 메서드 집합을 사용할 수 있습니다.

시간당 할 일

시작하다

Init(Aleksandr Petrov, 지게차 운전자' 12.95, 62);

{성, 직위 및 지불 금액 표시}

쇼;

끝;

개체의 필드에 대한 액세스는 이 개체의 메서드를 통해서만 수행된다는 점에 유의해야 합니다.

2. 개체 확장

불행히도 표준 파스칼은 완전히 다른 데이터 유형으로 작업할 수 있는 유연한 절차를 생성하기 위한 기능을 제공하지 않습니다. 객체 지향 프로그래밍은 상속을 통해 이 문제를 해결합니다. 파생 유형이 정의된 경우 상위 유형의 메서드가 상속되지만 원하는 경우 재정의할 수 있습니다. 상속된 메서드를 재정의하려면 상속된 메서드와 이름이 같지만 본문과 (필요한 경우) 매개 변수 집합이 다른 새 메서드를 선언하면 됩니다.

다음 예에서 시급을 받는 직원을 나타내는 TEmployee의 자식 유형을 정의해 보겠습니다.

const를

급여 기간 = 26; { 지불 기간 }

초과 근무 임계값 = 80; { 지불 기간 동안 }

초과 근무 계수 = 1.5; { 시간당 요금 }

유형

Tourly = 개체(TEmployee)

시간: 정수;

프로시저 초기화(AName, ATitle: 문자열; ARate:

실수, Atime: 정수);

함수 GetPayAmount : 실수;

끝;

절차 THourly.Init(AName, ATitle: 문자열;

ARate: 실수, Atime: 정수);

시작하다

TEmployee.Init(AName, ATitle, ARate);

시간 := A시간;

끝;

함수 THourly.GetPayAmount: 실제;

였다

초과 근무: 정수;

시작하다

초과 근무 := 시간 - 초과 근무 임계값;

초과 근무 > 0이면

GetPayAmount := RoundPay(OvertimeThreshold * 비율 +

초과 근무 비율 * 초과 근무 계수 * 비율)

그렇지 않으면

GetPayAmount := RoundPay(시간 * 요금)

끝;

시급을 받는 사람은 노동자입니다. 그는 TEmployee 객체를 정의하는 데 사용되는 모든 것(이름, 직위, 비율)을 가지고 있으며, 시급이 받는 금액만 해당 기간 동안 몇 시간 일했는지에 따라 다릅니다. 지급 기간. 따라서 Tourly에는 Time 필드도 필요합니다.

THourly는 새로운 Time 필드를 정의하기 때문에 초기화에는 시간과 상속된 필드를 모두 초기화하는 새로운 Init 메서드가 필요합니다. Name, Title, Rate와 같은 상속된 필드에 값을 직접 할당하는 대신 TEmployee 객체의 초기화 방법(첫 번째 THourly Init 문으로 설명)을 재사용하지 않는 이유는 무엇입니까?

재정의되는 메서드를 호출하는 것은 최상의 스타일이 아닙니다. 일반적으로 TEmployee.Init가 중요하지만 숨겨진 초기화를 수행할 수 있습니다.

재정의된 메서드를 호출할 때 파생된 개체 형식에 부모의 기능이 포함되어 있는지 확인해야 합니다. 또한 부모 메서드를 변경하면 모든 자식 메서드에 자동으로 영향을 줍니다.

TEmployee.Init를 호출한 후 THourly.Init는 자체 초기화를 수행할 수 있습니다. 이 경우에는 ATime에 전달된 값만 할당하는 것으로 구성됩니다.

재정의된 메서드의 또 다른 예는 시간당 직원의 지불 금액을 계산하는 THourly.GetPayAmount 함수입니다. 실제로 TEmployee 객체의 각 유형에는 고유한 GetPayAmount 메서드가 있습니다. 작업자 유형은 계산 방식에 따라 달라지기 때문입니다. THourly.GetPayAmount 메서드는 직원이 근무한 시간, 초과 근무 여부, 초과 근무 증가 요인 등을 고려해야 합니다.

T샐러리드 방식. GetPayAmount는 직원의 요율을 연간 지불 횟수로만 나누어야 합니다(이 예에서).

단위 노동자;

인터페이스

const를

급여 기간 = 26; {연도}

초과 근무 임계값 = 80; {각 지불 기간 동안}

초과 근무 계수=1.5; {정상 지불 대비 증가}

유형

직원 = 개체

이름, 제목: 문자열[25];

비율: 실제;

프로시저 Init(AName, ATitle: 문자열, ARate: Real);

함수 GetName : 문자열;

함수 GetTitle : 문자열;

함수 GetRate : 실제;

함수 GetPayAmount : 실수;

끝;

Tourly = 개체(TEmployee)

시간: 정수;

프로시저 초기화(AName, ATitle: 문자열; ARate:

실수, Atime: 정수);

함수 GetPayAmount : 실수;

함수 GetTime : 실제;

끝;

TSalared = 개체(TEmployee)

함수 GetPayAmount : 실수;

끝;

TCommissioned = 개체(TSalaried)

커미션 : 실제;

판매 금액 : 실제;

생성자 초기화(AName, ATitle: 문자열; ARate,

ACommission, ASalesAmount: Real);

함수 GetPayAmount : 실수;

끝;

이행

함수 RoundPay(임금: 실질) : 실질;

{보다 적은 금액을 무시하도록 지불금을 반올림

화폐 단위}

시작하다

RoundPay := Trunc(임금 * 100) / 100;

.

.

.

TEmployee는 객체 계층의 최상위이며 첫 번째 GetPayAmount 메서드를 포함합니다.

함수 TEmployee.GetPayAmount : 실수;

시작하다

실행 오류(211); { 런타임 오류 제공 }

끝;

메서드가 런타임 오류를 제공하는 것은 놀라운 일입니다. Employee.GetPayAmount를 호출하면 프로그램에 오류가 발생한다. 왜요? TEmployee는 객체 계층의 최상위이고 실제 작업자를 정의하지 않기 때문입니다. 따라서 TEmployee 메서드는 상속될 수 있지만 특정 방식으로 호출되지 않습니다. 우리의 모든 직원은 시간제, 급여 또는 조각 작업입니다. 런타임 오류는 프로그램 실행을 종료하고 추상 메서드 호출과 관련된 오류 메시지에 해당하는 211을 출력합니다(프로그램이 실수로 TEmployee.GetPayAmount를 호출하는 경우).

아래는 초과 근무 수당, 근무 시간 등을 고려하는 THourly.GetPayAmount 메서드입니다.

함수 THourly.GetPayAMount : 실제;

였다

초과 근무: 정수;

시작하다

초과 근무 := 시간 - 초과 근무 임계값;

초과 근무 > 0이면

GetPayAmount := RoundPay(OvertimeThreshold * 비율 +

초과 근무 비율 * 초과 근무 계수 * 비율)

그렇지 않으면

GetPayAmount := RoundPay(시간 * 요금)

끝;

TSalaried.GetPayAmount 메서드는 훨씬 간단합니다. 그것에 내기

지불 횟수로 나눈 값:

함수 TSalaried.GetPayAmount : 실수;

시작하다

GetPayAmount := RoundPay(요율 / PayPeriods);

끝;

TCommissioned.GetPayAmount 메서드를 보면 TSalaried.GetPayAmount를 호출하고 수수료를 계산하고 TSalaried 메서드에서 반환된 값에 추가하는 것을 볼 수 있습니다. GetPayAmount.

함수 TCommissioned.GetPayAmount : 실수;

시작하다

GetPayAmount := RoundPay(TSalaried.GetPayAmount +

수수료 * 판매 금액);

끝;

중요 참고 사항: 메서드는 재정의할 수 있지만 데이터 필드는 재정의할 수 없습니다. 데이터 필드가 개체 계층 구조에서 정의되면 정확히 동일한 이름을 가진 데이터 필드를 정의할 수 있는 하위 유형이 없습니다.

3. 객체 유형의 호환성

상속은 Borland Pascal의 유형 호환성 규칙을 어느 정도 수정합니다. 무엇보다도 파생 형식은 모든 부모 형식의 형식 호환성을 상속합니다.

이 확장된 유형 호환성은 세 가지 형식을 취합니다.

1) 객체의 구현 사이;

2) 객체 구현에 대한 포인터 사이;

3) 형식 매개변수와 실제 매개변수 사이.

그러나 세 가지 형식 모두에서 형식 호환성은 자식에서 부모로만 확장된다는 점을 기억하는 것이 매우 중요합니다. 즉, 자식 형식은 부모 형식 대신 자유롭게 사용할 수 있지만 그 반대의 경우는 불가능합니다.

예를 들어 TSalaried는 TEmployee의 자식이고 TSosh-missioned는 TSalaried의 자식입니다. 이를 염두에 두고 다음 설명을 고려하십시오.

유형

PEmployee = ^TEmployee;

PSalaried = ^TSSalared;

P커미션 = ^T 커미션됨;

였다

직원: TEmployee;

A샐러리드: TS샐러리드;

P커미션: T커미션;

TEmployeePtr: PEmployee;

TSalariedPtr: PSalaried;

TCommissionedPtr: PCommissioned;

이 설명에서 다음 연산자가 유효합니다.

과제:

직원 :=A샐러리드;

ASalared := ACommissioned;

TCommissionedPtr := ACommissioned;

주의

부모 개체에는 파생 형식의 인스턴스가 할당될 수 있습니다. 뒤로 할당은 허용되지 않습니다.

이 개념은 Pascal에 새로운 개념이며 처음에는 주문 유형 호환성이 어떤 것인지 기억하기 어려울 수 있습니다. 소스는 수신기를 완전히 채울 수 있어야 합니다. 파생 유형에는 상속 속성으로 인해 상위 유형에 포함된 모든 것이 포함됩니다. 따라서 파생 유형은 크기가 정확히 같거나(대부분의 경우) 상위 유형보다 크지만 결코 작지 않습니다. 부모(부모) 개체를 자식(자식)에게 할당하면 자식 개체의 일부 필드가 정의되지 않은 상태로 남을 수 있으며, 이는 위험하므로 불법입니다.

대입문에서 두 유형에 공통적인 필드만 소스에서 대상으로 복사됩니다. 할당 연산자에서:

AnEmployee:= ACommissioned;

TCommissioned 및 TEmployee에 공통되는 유일한 필드이므로 ACommissioned의 이름, 제목 및 요율 필드만 AnEmployee로 복사됩니다. 유형 호환성은 객체 유형에 대한 포인터 간에도 작동하며 객체 구현과 동일한 일반 규칙을 따릅니다. 자식에 대한 포인터는 부모에 대한 포인터에 할당될 수 있습니다. 이전 정의가 주어지면 다음 포인터 할당이 유효합니다.

TSalariedPtr:= TCommissionedPtr;

TEmployeePtr:= TSalariedPtr;

TEmployeePtr:= PCommissionedPtr;

역 할당은 허용되지 않는다는 것을 기억하십시오!

주어진 객체 유형의 형식 매개변수(값 또는 변수 매개변수)는 자체 유형의 객체 또는 모든 자식 유형의 객체를 실제 매개변수로 사용할 수 있습니다. 다음과 같이 프로시저 헤더를 정의하는 경우:

절차 CalcFedTax(피해자: TSalaried);

실제 매개변수 유형은 TSalared 또는 TCommissioned일 수 있지만 TEmployee는 아닙니다. 피해자도 변수가 될 수 있습니다. 이 경우 동일한 호환성 규칙을 따릅니다.

참고 :

값 매개변수와 변수 매개변수 사이에는 근본적인 차이가 있습니다. 값 매개변수는 매개변수로 전달된 실제 개체에 대한 포인터인 반면 변수 매개변수는 실제 매개변수의 복사본일 뿐입니다. 또한 이 사본에는 공식 값 매개변수의 유형에 포함된 필드만 포함됩니다. 이는 실제 매개변수가 문자 그대로 형식 매개변수의 유형으로 변환됨을 의미합니다. 가변 매개변수는 실제 매개변수가 변경되지 않은 상태로 유지된다는 점에서 패턴으로 캐스팅하는 것과 비슷합니다.

마찬가지로 형식 매개변수가 객체 유형에 대한 포인터인 경우 실제 매개변수는 해당 객체 유형이나 자식 유형에 대한 포인터일 수 있습니다. 절차의 제목을 지정하십시오.

프로시저 Worker.Add(AWorker: PSalared);

유효한 실제 매개변수 유형은 PSalaried 또는 PCommissioned이지만 PEmployee는 아닙니다.

강의 14번. 어셈블러

1. 어셈블러 정보

옛날 옛적에 어셈블러는 컴퓨터가 유용한 일을 하도록 만드는 것이 불가능하다는 것을 모르는 언어였습니다. 점차 상황이 바뀌었습니다. 컴퓨터와 더 편리한 통신 수단이 등장했습니다. 그러나 다른 언어와 달리 어셈블러는 죽지 않았고, 또한 원칙적으로 그렇게 할 수 없었습니다. 왜요? 답을 찾기 위해 일반적으로 어셈블리 언어가 무엇인지 이해하려고 노력할 것입니다.

요컨대, 어셈블리 언어는 기계어의 상징적 표현입니다. 가장 낮은 하드웨어 수준에서 기계의 모든 프로세스는 기계어의 명령(명령)에 의해서만 구동됩니다. 이를 통해 일반적인 이름에도 불구하고 컴퓨터 유형마다 어셈블리 언어가 다르다는 것이 분명합니다. 이것은 어셈블러로 작성된 프로그램의 모양과 이 언어가 반영하는 아이디어에도 적용됩니다.

하드웨어 관련 문제(또는 프로그램 속도 향상과 같은 하드웨어 관련 문제)를 실제로 해결하는 것은 어셈블러에 대한 지식 없이는 불가능합니다.

프로그래머나 다른 사용자는 가상 세계를 구축하기 위한 프로그램에 이르기까지 모든 고급 도구를 사용할 수 있으며 아마도 컴퓨터가 실제로 프로그램이 작성된 언어의 명령이 아니라 변환된 표현을 실행하고 있다고 의심조차 할 수 없습니다. 완전히 다른 언어인 기계어의 지루하고 지루한 시퀀스 명령의 형태로. 이제 그러한 사용자에게 비표준 문제가 있다고 상상해보십시오. 예를 들어 그의 프로그램은 비정상적인 장치와 함께 작동하거나 컴퓨터 하드웨어의 원리에 대한 지식이 필요한 다른 작업을 수행해야 합니다. 프로그래머가 자신의 프로그램을 작성한 언어가 아무리 훌륭해도 어셈블러를 모르면 할 수 없습니다. 그리고 고급 언어의 거의 모든 컴파일러가 어셈블러의 모듈과 모듈을 연결하는 수단을 포함하거나 어셈블러 프로그래밍 수준에 대한 액세스를 지원하는 것은 우연이 아닙니다.

컴퓨터는 여러 물리적 장치로 구성되며 각 장치는 시스템 장치라는 하나의 장치에 연결됩니다. 기능적 목적을 이해하기 위해 일반적인 컴퓨터의 블록 다이어그램을 살펴보겠습니다(그림 1). 그것은 절대적인 정확성을 가장하지 않으며 현대 개인용 컴퓨터 요소의 목적, 상호 연결 및 일반적인 구성을 보여주는 것을 목표로 합니다.

쌀. 1. 개인용 컴퓨터의 구조도

2. 마이크로프로세서의 소프트웨어 모델

오늘날의 컴퓨터 시장에는 다양한 유형의 컴퓨터가 있습니다. 따라서 특정 유형(또는 모델)의 컴퓨터 기능과 다른 유형(모델)의 컴퓨터와의 고유한 기능을 평가하는 방법에 대해 소비자가 질문을 할 것이라고 가정할 수 있습니다. 기능적 프로그램 제어 속성 측면에서 컴퓨터를 특징 짓는 모든 개념을 모으기 위해 컴퓨터 아키텍처라는 특수 용어가 있습니다. 3세대 기계의 비교 평가를 위해 처음으로 컴퓨터 아키텍처의 개념이 언급되기 시작했습니다.

컴퓨터의 어떤 부분이 표시되고 이 언어로 프로그래밍할 수 있는지 알아낸 후에만 컴퓨터의 어셈블리 언어 학습을 시작하는 것이 이치에 맞습니다. 이것은 프로그래머가 사용할 수 있는 XNUMX개의 레지스터를 포함하는 마이크로프로세서 프로그램 모델의 일부인 소위 컴퓨터 프로그램 모델입니다.

이러한 레지스터는 두 개의 큰 그룹으로 나눌 수 있습니다.

1) 사용자 등록 6개;

2) 16개의 시스템 레지스터.

3. 사용자 등록

이름에서 알 수 있듯이 사용자 레지스터는 프로그래머가 프로그램을 작성할 때 사용할 수 있기 때문에 호출됩니다. 이러한 레지스터에는 다음이 포함됩니다(그림 2).

1) 프로그래머가 데이터와 주소를 저장하는 데 사용할 수 있는 32개의 XNUMX비트 레지스터(범용 레지스터(RON)라고도 함):

eax/ax/아/알;

ebx/bx/bh/bl;

edx/dx/dh/dl;

ecx/cx/ch/cl;

ebp/bp;

esi/si;

에디/디;

특히/sp.

2) XNUMX개의 세그먼트 레지스터: cs, ds, ss, es, fs, gs;

3) 상태 및 제어 레지스터:

플래그 레지스터 eflags/플래그;

eip/ip 명령 포인터 레지스터.

쌀. 2. 사용자 등록

이러한 레지스터의 대부분은 슬래시와 함께 제공됩니다. 이들은 서로 다른 레지스터가 아니라 하나의 큰 32비트 레지스터의 일부입니다. 프로그램에서 별도의 개체로 사용할 수 있습니다.

4. 일반 레지스터

이 그룹의 모든 레지스터를 사용하면 "하단" 부분에 액세스할 수 있습니다. 이러한 레지스터의 하위 16비트 및 8비트 부분만 자체 주소 지정에 사용할 수 있습니다. 이러한 레지스터의 상위 16비트는 독립 개체로 사용할 수 없습니다.

범용 레지스터 그룹에 속하는 레지스터를 나열해 보겠습니다. 이러한 레지스터는 산술 논리 장치(AL>) 내부의 마이크로프로세서에 물리적으로 위치하므로 ALU 레지스터라고도 합니다.

1) eax/ax/ah/al (누산기 레지스터) - 배터리. 중간 데이터를 저장하는 데 사용됩니다. 일부 명령에서는 이 레지스터를 사용해야 합니다.

2) ebx/bx/bh/bl (기본 레지스터) - 기본 레지스터. 메모리에 일부 개체의 기본 주소를 저장하는 데 사용됩니다.

3) ecx/cx/ch/cl (카운트 레지스터) - 카운터 레지스터. 일부 반복 작업을 수행하는 명령에 사용됩니다. 그것의 사용은 종종 암시적이며 해당 명령의 알고리즘에 숨겨져 있습니다.

예를 들어 루프 구성 명령은 특정 주소에 있는 명령으로 제어를 이전하는 것 외에도 esx/cx 레지스터의 값을 분석하고 XNUMX씩 감소시킵니다.

4) edx/dx/dh/dl (데이터 레지스터) - 데이터 레지스터.

eax/ax/ah/al 레지스터와 마찬가지로 중간 데이터를 저장합니다. 일부 명령은 사용이 필요합니다. 일부 명령의 경우 이것은 암시적으로 발생합니다.

다음 두 레지스터는 소위 체인 작업, 즉 각각 32, 16 또는 8비트 길이가 될 수 있는 요소 체인을 순차적으로 처리하는 작업을 지원하는 데 사용됩니다.

1) esi/si(소스 인덱스 레지스터) - 소스 인덱스.

체인 작업의 이 레지스터에는 소스 체인에 있는 요소의 현재 주소가 포함됩니다.

2) edi/di(목적지 색인 레지스터) - 수신자(수신자)의 색인. 체인 작업의 이 레지스터에는 대상 체인의 현재 주소가 포함됩니다.

하드웨어 및 소프트웨어 수준의 마이크로 프로세서 아키텍처에서는 스택과 같은 데이터 구조가 지원됩니다. 마이크로 프로세서 명령 시스템의 스택으로 작업하기 위해 특수 명령이 있으며 마이크로 프로세서 소프트웨어 모델에는 이에 대한 특수 레지스터가 있습니다.

1) esp/sp (스택 포인터 레지스터) - 스택 포인터 레지스터. 현재 스택 세그먼트의 스택 상단에 대한 포인터를 포함합니다.

2) ebp/bp(기본 포인터 레지스터) - 스택 프레임 기본 포인터 레지스터. 스택 내부의 데이터에 대한 임의 액세스를 구성하도록 설계되었습니다.

일부 명령어에 대한 레지스터의 고정 고정을 사용하면 기계 표현을 보다 간결하게 인코딩할 수 있습니다. 이러한 기능을 알면 필요한 경우 프로그램 코드가 차지하는 메모리의 최소 몇 바이트를 절약할 수 있습니다.

5. 세그먼트 레지스터

마이크로프로세서 소프트웨어 모델에는 XNUMX개의 세그먼트 레지스터가 있습니다: cs, ss, ds, es, gs, fs.

그들의 존재는 조직의 특성과 Intel 마이크로 프로세서의 RAM 사용으로 인한 것입니다. 마이크로프로세서 하드웨어가 세그먼트라고 하는 세 부분의 형태로 프로그램의 구조적 구성을 지원한다는 사실에 있습니다. 따라서 이러한 메모리 구성을 세그먼트라고합니다.

프로그램이 특정 시점에 액세스할 수 있는 세그먼트를 나타내기 위해 세그먼트 레지스터가 사용됩니다. 실제로 (약간의 수정이 있음) 이러한 레지스터에는 해당 세그먼트가 시작되는 메모리 주소가 포함되어 있습니다. 기계 명령어를 처리하는 논리는 잘 정의된 세그먼트 레지스터의 주소가 명령어를 가져오거나 프로그램 데이터에 액세스하거나 스택에 액세스할 때 암시적으로 사용되는 방식으로 구성됩니다.

마이크로프로세서는 다음 유형의 세그먼트를 지원합니다.

1. 코드 세그먼트. 프로그램 명령을 포함합니다. 이 세그먼트에 액세스하려면 cs 레지스터(코드 세그먼트 레지스터)인 세그먼트 코드 레지스터가 사용됩니다. 여기에는 마이크로프로세서가 액세스할 수 있는 기계 명령어 세그먼트의 주소가 포함됩니다(즉, 이러한 명령어는 마이크로프로세서 파이프라인에 로드됨).

2. 데이터 세그먼트. 프로그램에서 처리한 데이터를 포함합니다. 이 세그먼트에 액세스하기 위해 ds(데이터 세그먼트 레지스터) 레지스터가 사용됩니다. 이 레지스터는 현재 프로그램의 데이터 세그먼트 주소를 저장하는 세그먼트 데이터 레지스터입니다.

3. 스택 세그먼트. 이 세그먼트는 스택이라고 하는 메모리 영역입니다. 마이크로프로세서는 다음 원칙에 따라 스택 작업을 구성합니다. 이 영역에 기록된 마지막 요소가 먼저 선택됩니다. 이 세그먼트에 액세스하기 위해 ss 레지스터(스택 세그먼트 레지스터)가 사용됩니다. 스택 세그먼트의 주소를 포함하는 스택 세그먼트 레지스터입니다.

4. 추가 데이터 세그먼트. 암묵적으로 대부분의 기계 명령어를 실행하는 알고리즘은 처리하는 데이터가 데이터 세그먼트에 있으며 그 주소는 ds 세그먼트 레지스터에 있다고 가정합니다. 프로그램에 하나의 데이터 세그먼트가 충분하지 않은 경우 세 개의 추가 데이터 세그먼트를 사용할 수 있습니다. 그러나 주소가 ds 세그먼트 레지스터에 포함된 기본 데이터 세그먼트와 달리 추가 데이터 세그먼트를 사용할 때는 명령에서 특수 세그먼트 재정의 접두사를 사용하여 해당 주소를 명시적으로 지정해야 합니다. 추가 데이터 세그먼트의 주소는 레지스터 es, gs, fs(확장 데이터 세그먼트 레지스터)에 포함되어야 합니다.

6. 상태 및 제어 레지스터

마이크로프로세서에는 마이크로프로세서 자체와 현재 파이프라인에 명령이 로드된 프로그램 모두의 상태에 대한 정보를 지속적으로 포함하는 여러 레지스터가 있습니다. 이러한 레지스터에는 다음이 포함됩니다.

1) 플래그 레지스터 플래그/플래그;

2) eip/ip 명령 포인터 레지스터.

이 레지스터를 사용하여 명령 실행 결과에 대한 정보를 얻고 마이크로프로세서 자체의 상태에 영향을 줄 수 있습니다. 이러한 레지스터의 목적과 내용을 더 자세히 살펴보겠습니다.

1. eflags/flags(플래그 레지스터) - 플래그 레지스터. eflags/플래그의 비트 심도는 32/16비트입니다. 이 레지스터의 개별 비트는 특정 기능적 목적을 가지며 플래그라고 합니다. 이 레지스터의 하단 부분은 18086의 플래그 레지스터와 정확히 동일합니다. 그림 3은 eflags 레지스터의 내용을 보여줍니다.

쌀. 3. eflags 레지스터의 내용

플래그/플래그 레지스터의 플래그는 사용 방법에 따라 세 그룹으로 나눌 수 있습니다.

1) XNUMX개의 상태 플래그.

이 플래그는 기계 명령어가 실행된 후에 변경될 수 있습니다. eflags 레지스터의 상태 플래그는 산술 또는 논리 연산 실행 결과의 세부 사항을 반영합니다. 이를 통해 계산 프로세스의 상태를 분석하고 조건부 점프 명령 및 서브루틴 호출을 사용하여 응답할 수 있습니다. 표 1에는 상태 플래그와 용도가 나열되어 있습니다.

2) 하나의 제어 플래그.

df(디렉토리 플래그)로 표시됩니다. eflags 레지스터의 비트 10에 있으며 연결된 명령에 의해 사용됩니다. df 플래그의 값은 이러한 작업에서 요소별 처리 방향을 결정합니다. 문자열의 시작에서 끝까지(df = 0) 또는 그 반대로, 문자열의 끝에서 시작으로(df = 1). df 플래그 작업을 위한 특수 명령이 있습니다: eld(df 플래그 제거) 및 std(df 플래그 설정). 이러한 명령을 사용하면 알고리즘에 따라 df 플래그를 조정하고 문자열에 대한 작업을 수행할 때 카운터가 자동으로 증가하거나 감소하도록 할 수 있습니다.

3) XNUMX개의 시스템 플래그.

I/O, 마스크 가능한 인터럽트, 디버깅, 작업 전환 및 8086 가상 모드를 제어합니다.응용 프로그램이 이러한 플래그를 불필요하게 수정하는 것은 대부분의 경우 프로그램이 종료될 수 있으므로 권장하지 않습니다. 표 2에는 시스템 플래그와 용도가 나열되어 있습니다.

표 1. 상태 플래그표 2. 시스템 플래그

2. eip/ip(Instraction Pointer register) - 명령어 포인터 레지스터. eip/ip 레지스터의 너비는 32/16비트이며 현재 명령 세그먼트에 있는 cs 세그먼트 레지스터의 내용과 관련하여 실행할 다음 명령의 오프셋을 포함합니다. 이 레지스터는 프로그래머가 직접 액세스할 수 없지만 조건부 및 무조건 점프, 프로시저 호출 및 프로시저에서 반환에 대한 명령을 포함하는 다양한 제어 명령에 의해 값이 로드되고 변경됩니다. 인터럽트가 발생하면 eip/ip 레지스터도 수정됩니다.

강의 15번. 레지스터

1. 마이크로프로세서 시스템 레지스터

이러한 레지스터의 이름은 시스템에서 특정 기능을 수행함을 나타냅니다. 시스템 레지스터의 사용은 엄격하게 규제됩니다. 보호 모드를 제공하는 것은 바로 그들입니다. 그들은 또한 자격을 갖춘 시스템 프로그래머가 가장 낮은 수준의 작업을 수행할 수 있도록 의도적으로 보이게 남겨둔 마이크로프로세서 아키텍처의 일부로 생각할 수 있습니다.

시스템 레지스터는 세 그룹으로 나눌 수 있습니다.

1) XNUMX개의 제어 레지스터;

2) 시스템 주소의 XNUMX개 레지스터;

3) XNUMX개의 디버그 레지스터.

2. 제어 레지스터

제어 레지스터 그룹에는 0개의 레지스터(cr1, cr2, cr3, cr0)가 포함됩니다. 이 레지스터는 일반적인 시스템 제어를 위한 것입니다. 제어 레지스터는 권한 수준이 XNUMX인 프로그램에서만 사용할 수 있습니다.

마이크로프로세서에는 1개의 제어 레지스터가 있지만 그 중 XNUMX개만 사용할 수 있습니다. crXNUMX은 제외되고 기능은 아직 정의되지 않았습니다(나중에 사용하기 위해 예약됨).

cr0 레지스터에는 마이크로프로세서의 작동 모드를 제어하고 수행 중인 특정 작업에 관계없이 해당 상태를 전역적으로 반영하는 시스템 플래그가 포함되어 있습니다.

시스템 플래그의 목적:

1) pe(보호 활성화), 비트 0 - 보호 작동 모드를 활성화합니다. 이 플래그의 상태는 실제(pe = 0) 또는 보호(pe = 1)의 두 모드 중 어떤 모드에서 마이크로프로세서가 주어진 시간에 작동하는지 보여줍니다.

2) mp(Math Present), 비트 1 - 보조 프로세서의 존재. 항상 1;

3) ts(작업 전환), 비트 3 - 작업 전환. 프로세서는 다른 작업으로 전환할 때 이 비트를 자동으로 설정합니다.

4) am(정렬 마스크), 비트 18 - 정렬 마스크. 이 비트는 정렬 제어를 활성화(am = 1) 또는 비활성화(am = 0)합니다.

5) cd(캐시 비활성화), 비트 30 - 캐시 메모리를 비활성화합니다.

이 비트를 사용하여 내부 캐시(첫 번째 수준 캐시)의 사용을 비활성화(cd =1) 또는 활성화(cd = 0)할 수 있습니다.

6) pg(PaGing), 비트 31 - 페이징을 활성화(pg=1) 또는 비활성화(pg=0)합니다.

플래그는 메모리 구성의 페이징 모델에서 사용됩니다.

cr2 레지스터는 현재 명령어가 현재 메모리에 없는 메모리 페이지에 포함된 주소에 액세스했을 때 상황을 등록하기 위해 RAM 페이징에서 사용됩니다.

이러한 상황에서 예외 번호 14가 마이크로 프로세서에서 발생하고 이 예외를 일으킨 명령어의 선형 32비트 주소가 레지스터 cr2에 기록됩니다. 이 정보를 사용하여 예외 핸들러(14)는 원하는 페이지를 결정하고 이를 메모리로 교체하고 프로그램의 정상 작동을 재개합니다.

cr3 레지스터는 페이징 메모리에도 사용됩니다. 이것은 소위 20단계 페이지 디렉토리 레지스터입니다. 여기에는 현재 작업 페이지 디렉터리의 1024비트 물리적 기본 주소가 포함됩니다. 이 디렉토리에는 32개의 1024비트 디스크립터가 포함되어 있으며 각 디스크립터에는 두 번째 레벨 페이지 테이블의 주소가 포함되어 있습니다. 차례로, 두 번째 레벨 페이지 테이블 각각은 메모리의 페이지 프레임을 지정하는 32개의 4비트 설명자를 포함합니다. 페이지 프레임 크기는 XNUMXKB입니다.

3. 시스템 주소 레지스터

이러한 레지스터를 메모리 관리 레지스터라고도 합니다.

마이크로프로세서의 멀티태스킹 모드에서 프로그램과 데이터를 보호하도록 설계되었습니다. 마이크로프로세서 보호 모드에서 작동할 때 주소 공간은 다음과 같이 나뉩니다.

1) 전역 - 모든 작업에 공통입니다.

2) 로컬 - 각 작업에 대해 분리됩니다.

이러한 분리는 마이크로프로세서 아키텍처에서 다음과 같은 시스템 레지스터의 존재를 설명합니다.

1) 48비트 크기를 갖고 GDT 글로벌 디스크립터 테이블의 32비트(비트 16-47) 기본 주소와 16비트(비트)를 포함하는 글로벌 디스크립터 테이블 gdtr(글로벌 디스크립터 테이블 레지스터)의 레지스터 0-15) GDT 테이블의 바이트 크기인 한계값;

2) 로컬 설명자 테이블 레지스터 ldtr(Local Descriptor Table Register), 크기는 16비트이고 로컬 설명자 테이블 LDT의 설명자의 소위 선택자를 포함합니다. 이 선택자는 GDT 테이블의 포인터로, 다음을 설명합니다. 로컬 디스크립터 테이블 LDT를 포함하는 세그먼트;

3) 인터럽트 디스크립터 테이블 idtr(인터럽트 디스크립터 테이블 레지스터)의 레지스터, 크기가 48비트이고 IDT 인터럽트 디스크립터 테이블의 32비트(비트 16-47) 기본 주소와 16비트(비트)를 포함합니다. 0-15) IDT 테이블의 바이트 크기인 한계값;

4) 16비트 태스크 레지스터 tr(태스크 레지스터), ldtr 레지스터와 마찬가지로 선택자, 즉 GDT 테이블의 디스크립터에 대한 포인터를 포함합니다. 이 디스크립터는 현재 태스크 세그먼트 상태(TSS)를 설명합니다. 이 세그먼트는 시스템의 각 작업에 대해 생성되며 엄격하게 규제되는 구조를 가지며 작업의 컨텍스트(현재 상태)를 포함합니다. TSS 세그먼트의 주요 목적은 다른 작업으로 전환하는 순간 작업의 현재 상태를 저장하는 것입니다.

4. ​​디버그 레지스터

이것은 하드웨어 디버깅을 위한 매우 흥미로운 레지스터 그룹입니다. 하드웨어 디버깅 도구는 i486 마이크로프로세서에 처음 등장했습니다. 하드웨어에서 마이크로프로세서에는 XNUMX개의 디버그 레지스터가 포함되어 있지만 실제로는 그 중 XNUMX개만 사용됩니다.

dr0, dr1, dr2, dr3 레지스터는 32비트 너비를 가지며 0개 중단점의 선형 주소를 설정하도록 설계되었습니다. 이 경우에 사용되는 메커니즘은 다음과 같습니다. 현재 프로그램에 의해 생성된 모든 주소는 레지스터 dr3...dr1의 주소와 비교되고, 일치하는 경우 번호 XNUMX의 디버깅 예외가 생성됩니다.

레지스터 dr6을 디버그 상태 레지스터라고 합니다. 이 레지스터의 비트는 마지막 예외 번호 1이 발생한 이유에 따라 설정됩니다.

우리는 이러한 비트와 그 목적을 나열합니다.

1) b0 - 이 비트가 1로 설정되면 레지스터 dr0에 정의된 체크포인트에 도달한 결과로 마지막 예외(인터럽트)가 발생했습니다.

2) b1 - b0과 유사하지만 레지스터 dr1의 체크포인트용입니다.

3) b2 - b0과 유사하지만 레지스터 dr2의 체크포인트용입니다.

4) bЗ - b0과 유사하지만 레지스터 dr3의 체크포인트용;

5) bd(비트 13) - 디버그 레지스터를 보호하는 역할을 합니다.

6) bs(비트 14) - 예외 1이 플래그 레지스터의 플래그 tf = 1 상태로 인해 발생한 경우 1로 설정됩니다.

7) bt(비트 15)는 예외 1이 TSS t = 1에서 트랩 비트가 설정된 작업으로의 전환으로 인해 발생한 경우 1로 설정됩니다.

이 레지스터의 다른 모든 비트는 1으로 채워집니다. 예외 처리기 6은 drXNUMX의 내용을 기반으로 예외의 원인을 파악하고 필요한 조치를 취해야 합니다.

레지스터 dr7을 디버그 제어 레지스터라고 합니다. 여기에는 인터럽트가 생성되어야 하는 다음 조건을 지정할 수 있는 XNUMX개의 디버그 중단점 레지스터 각각에 대한 필드가 포함되어 있습니다.

1) 체크포인트 등록 위치 - 현재 작업 또는 모든 작업에서만. 이 비트는 레지스터 dr8의 하위 7비트를 차지합니다(각각 레지스터 dr2, dr0, dr1, dr2에 의해 설정된 각 중단점(실제로는 중단점)에 대해 3비트).

각 쌍의 첫 번째 비트는 소위 로컬 해상도입니다. 이를 설정하면 현재 작업의 주소 공간 내에 있는 경우 중단점이 적용되도록 지시합니다.

각 쌍의 두 번째 비트는 지정된 중단점이 시스템의 모든 작업 주소 공간 내에서 유효함을 나타내는 전역 권한을 지정합니다.

2) 인터럽트가 시작되는 액세스 유형: 명령을 가져올 때, 쓸 때 또는 데이터를 쓰거나 읽을 때만. 인터럽트 발생의 이러한 특성을 결정하는 비트는 이 레지스터의 상단에 있습니다. 대부분의 시스템 레지스터는 프로그래밍 방식으로 액세스할 수 있습니다.

강의 16번. 어셈블러 프로그램

1. 어셈블러의 프로그램 구조

어셈블리 언어 프로그램은 메모리 세그먼트라고 하는 메모리 블록 모음입니다. 프로그램은 이러한 블록 세그먼트 중 하나 이상으로 구성될 수 있습니다. 각 세그먼트에는 언어 문장 모음이 포함되어 있으며 각 문장은 별도의 프로그램 코드 행을 차지합니다.

어셈블리 문에는 네 가지 유형이 있습니다.

1) 기계 명령의 상징적 유사어인 명령 또는 지침. 번역 과정에서 어셈블리 명령어는 마이크로프로세서 명령어 세트의 해당 명령어로 변환됩니다.

2) 매크로. 특정 방식으로 형식화되고 방송 중에 다른 문장으로 대체되는 프로그램 텍스트의 문장입니다.

3) 어셈블러 변환기가 특정 작업을 수행하도록 지시하는 지시문. 지시문에는 기계 표현에 상응하는 항목이 없습니다.

4) 러시아 알파벳 문자를 포함하여 모든 문자를 포함하는 주석 행. 주석은 번역자에 의해 무시됩니다.

2. 어셈블리 구문

프로그램을 구성하는 문장은 명령, 매크로, 지시문 또는 주석에 해당하는 구문 구조일 수 있습니다. 어셈블러 번역기가 이를 인식하려면 특정 구문 규칙에 따라 구성되어야 합니다. 이렇게 하려면 문법 규칙과 같은 언어 구문에 대한 공식적인 설명을 사용하는 것이 가장 좋습니다. 이러한 방식으로 프로그래밍 언어를 설명하는 가장 일반적인 방법은 구문 다이어그램과 확장된 Backus-Naur 형식입니다. 실제 사용을 위해서는 구문 다이어그램이 더 편리합니다. 예를 들어 어셈블리 언어 문의 구문은 다음 그림에 표시된 구문 다이어그램을 사용하여 설명할 수 있습니다.

쌀. 4. 어셈블러 문장 형식

쌀. 5. 지시 형식

쌀. 6. 명령 및 매크로 형식

이 도면에서:

1) 레이블 이름 - 식별자, 그 값은 그것이 나타내는 프로그램의 소스 코드 문장의 첫 번째 바이트 주소입니다.

2) 이름 - 이 지시어를 동일한 이름의 다른 지시어와 구별하는 식별자. 어셈블러가 특정 지시문을 처리한 결과 이 ​​이름에 특정 특성을 할당할 수 있습니다.

3) 연산 코드(COP) 및 지시어는 해당 기계 명령어, 매크로 명령어 또는 변환기 지시어의 니모닉 지정입니다.

4) 피연산자 - 작업이 수행되는 개체를 나타내는 명령, 매크로 또는 어셈블러 지시문의 일부입니다. 어셈블러 피연산자는 연산 부호와 일부 예약어를 사용하는 숫자 및 텍스트 상수, 변수 레이블 및 식별자가 있는 식으로 설명됩니다.

구문 다이어그램을 사용하는 방법? 매우 간단합니다. 다이어그램의 입력(왼쪽)에서 출력(오른쪽)까지의 경로를 찾아서 따라가기만 하면 됩니다. 그러한 경로가 존재하면 문장이나 구성이 구문적으로 올바른 것입니다. 그러한 경로가 없으면 컴파일러는 이 생성을 허용하지 않습니다. 구문 다이어그램으로 작업할 때 경로 중에 오른쪽에서 왼쪽으로 따를 수 있는 경로가 있을 수 있으므로 화살표로 표시된 순회 방향에 주의하십시오. 실제로 구문 다이어그램은 프로그램의 입력 문장을 구문 분석할 때 번역기의 논리를 반영합니다.

프로그램 텍스트를 작성할 때 허용되는 문자는 다음과 같습니다.

1) 모든 라틴 문자: A - Z, a - z. 이 경우 대문자와 소문자는 동일한 것으로 간주됩니다.

2) 0에서 9까지의 숫자;

3) 기호 ?, @, S, _, &;

4) 분리기.

어셈블러 문장은 구문적으로 분리할 수 없는 유효한 언어 기호 시퀀스인 어휘소로 구성되며 번역자에게 의미가 있습니다.

토큰은 다음과 같습니다.

1. 식별자 - 작업 코드, 변수 이름 및 레이블 이름과 같은 프로그램 개체를 지정하는 데 사용되는 유효한 문자 시퀀스입니다. 식별자 작성 규칙은 다음과 같습니다. 식별자는 하나 이상의 문자로 구성될 수 있습니다. 문자로는 라틴 알파벳 문자, 숫자 및 일부 특수 문자(_, ?, $, @)를 사용할 수 있습니다. 식별자는 숫자로 시작할 수 없습니다. 식별자의 길이는 최대 255자까지 가능하지만 변환기는 처음 32자만 받아들이고 나머지는 무시합니다. mv 명령줄 옵션을 사용하여 가능한 식별자의 길이를 조정할 수 있습니다. 또한 번역자에게 대문자와 소문자를 구별하거나 차이점을 무시하도록 지시할 수 있습니다(기본적으로 수행됨). 이를 위해 /mu, /ml, /mx 명령줄 옵션이 사용됩니다.

2. 일련의 문자 - 작은따옴표 또는 큰따옴표로 묶인 일련의 문자입니다.

3. XNUMX진수, XNUMX진수, XNUMX진수 중 하나의 정수. 어셈블러 프로그램에서 숫자를 작성할 때 숫자 식별은 특정 규칙에 따라 수행됩니다.

1) 십진수는 예를 들어 25 또는 139와 같은 추가 문자를 식별할 필요가 없습니다.

2) 프로그램의 소스 텍스트에서 이진수를 식별하려면 이를 구성하는 10010101과 XNUMX을 쓴 후 라틴어 "b"를 입력해야 합니다(예: XNUMX b).

3) XNUMX진수는 작성할 때 더 많은 규칙을 갖습니다.

a) 첫째, 숫자 0~9, 라틴 알파벳 a, b, c, d, e, Gili D B, C, D, E, E의 소문자 및 대문자로 구성됩니다.

b) 두 번째로, 0진수는 숫자 9...190845(예: 5)로만 구성되거나 라틴 알파벳 문자(예: efl0 )로 시작할 수 있기 때문에 번역자가 5진수를 인식하는 데 어려움을 겪을 수 있습니다. 주어진 토큰이 XNUMX진수나 식별자가 아니라는 것을 번역자에게 "설명"하기 위해 프로그래머는 특별한 방법으로 XNUMX진수를 강조 표시해야 합니다. 이렇게 하려면 XNUMX진수를 구성하는 XNUMX진수 시퀀스 끝에 라틴 문자 "h"를 쓰세요. 이것은 필수입니다. XNUMX진수가 문자로 시작하면 그 앞에 XNUMX이 붙습니다: XNUMX eflXNUMX h.

따라서 우리는 어셈블러 프로그램의 문장이 어떻게 구성되는지 알아냈습니다. 그러나 이것은 가장 피상적인 견해일 뿐이다.

거의 모든 문장에는 일부 작업이 수행되는 개체에 대한 설명이 포함되어 있습니다. 이러한 개체를 피연산자라고 합니다. 다음과 같이 정의할 수 있습니다. 피연산자는 명령 또는 지시문의 영향을 받는 개체(일부 값, 레지스터 또는 메모리 셀)이거나 명령 또는 지시문의 동작을 정의하거나 구체화하는 개체입니다.

피연산자는 산술, 논리, 비트 및 특성 연산자와 결합하여 일부 값을 계산하거나 지정된 명령이나 지시문의 영향을 받는 메모리 위치를 결정할 수 있습니다.

다음 분류에서 피연산자의 특성을 보다 자세히 살펴보겠습니다.

1) 상수 또는 즉시 피연산자 - 고정 값을 갖는 숫자, 문자열, 이름 또는 표현식. 이름은 재배치할 수 없어야 합니다. 즉, 메모리에 로드할 프로그램의 주소에 의존하지 않아야 합니다. 예를 들어 등호 또는 = 연산자로 정의할 수 있습니다.

2) 주소 피연산자, 주소의 두 가지 구성 요소인 세그먼트와 오프셋을 지정하여 메모리에서 피연산자의 물리적 위치를 지정합니다(그림 7).

쌀. 7. 주소 피연산자의 설명 구문

3) 재배치 가능한 피연산자 - 일부 메모리 주소를 나타내는 기호 이름. 이러한 주소는 일부 명령어(피연산자가 레이블인 경우) 또는 데이터(피연산자가 데이터 세그먼트의 메모리 위치 이름인 경우)의 메모리 위치를 나타낼 수 있습니다.

재배치 가능한 피연산자는 특정 물리적 메모리 주소에 연결되어 있지 않다는 점에서 주소 피연산자와 다릅니다. 이동 중인 피연산자 주소의 세그먼트 구성 요소는 알 수 없으며 프로그램이 실행을 위해 메모리에 로드된 후에 결정됩니다.

주소 카운터는 특정 종류의 피연산자입니다. 기호 S로 표시됩니다. 이 피연산자의 특수성은 어셈블러 번역기가 소스 프로그램에서 이 기호를 발견하면 주소 카운터의 현재 값을 대신 대체한다는 것입니다. 주소 카운터 또는 배치 카운터라고도 하는 값은 코드 세그먼트의 시작 부분에서 현재 기계 명령어의 오프셋입니다. 목록 형식에서 두 번째 또는 세 번째 열은 주소 카운터에 해당합니다(중첩 수준이 있는 열이 목록에 있는지 여부에 따라 다름). 어떤 목록을 예로 들면 번역기가 다음 어셈블러 명령어를 처리할 때 생성된 기계 명령어의 길이만큼 주소 카운터가 증가하는 것을 볼 수 있습니다. 이 점을 올바르게 이해하는 것이 중요합니다. 예를 들어 어셈블러 지시문을 처리해도 카운터는 변경되지 않습니다. 지시어는 어셈블러 명령과 달리 프로그램의 기계 표현을 형성하기 위해 특정 작업을 수행하도록 컴파일러에 지시할 뿐이며 컴파일러는 메모리에 어떤 구조도 생성하지 않습니다.

이러한 식을 사용하여 점프할 때 주소 카운터의 값은 이 명령어 뒤에 오는 명령어가 아니라 이 명령어의 명령어 세그먼트에 있는 오프셋에 해당하므로 이 식이 사용되는 명령어 자체의 길이에 유의하십시오. . 이 예에서 jmp 명령은 2바이트를 사용합니다. 그러나 명령어의 길이는 사용하는 피연산자에 따라 달라집니다. 레지스터 피연산자가 있는 명령어는 피연산자 중 하나가 메모리에 있는 명령어보다 짧습니다. 대부분의 경우 이 정보는 기계 명령어의 형식을 알고 명령어의 목적 코드가 포함된 목록 열을 분석하여 얻을 수 있습니다.

4) 레지스터 피연산자는 단지 레지스터 이름입니다. 어셈블러 프로그램에서는 모든 범용 레지스터와 대부분의 시스템 레지스터의 이름을 사용할 수 있습니다.

5) 기본 및 색인 피연산자. 이 피연산자 유형은 간접 기본, 간접 인덱스 주소 지정 또는 이들의 조합 및 확장을 구현하는 데 사용됩니다.

6) 구조 피연산자는 구조라고 하는 복잡한 데이터 유형의 특정 요소에 액세스하는 데 사용됩니다.

레코드(구조체 유형과 유사)는 일부 레코드의 비트 필드에 액세스하는 데 사용됩니다.

피연산자는 연산이 수행되는 개체를 나타내는 기계 명령어의 일부를 형성하는 기본 구성 요소입니다. 보다 일반적인 경우 피연산자는 식이라는 보다 복잡한 형식의 구성 요소로 포함될 수 있습니다. 표현식은 전체적으로 고려되는 피연산자와 연산자의 조합입니다. 식 평가 결과는 일부 메모리 셀의 주소 또는 일부 상수(절대) 값이 될 수 있습니다.

가능한 피연산자 유형을 이미 고려했습니다. 이제 가능한 어셈블러 연산자 유형과 어셈블러 식 형성을 위한 구문 규칙을 나열하고 연산자에 대해 간략하게 설명합니다.

1. 산술 연산자. 여기에는 다음이 포함됩니다.

1) 단항 "+" 및 "-";

2) 이진 "+" 및 "-";

3) 곱셈 "*";

4) 정수 나누기 "/";

5) 부서 "mod"에서 나머지를 얻습니다.

이러한 연산자는 표 6,7,8의 우선 순위 수준 4에 있습니다.

쌀. 8. 산술 연산 구문

2. 시프트 연산자는 지정된 비트 수만큼 식을 시프트합니다(그림 9).

쌀. 9. 시프트 연산자의 구문

3. 비교 연산자(값 "true" 또는 "false" 반환)는 논리식을 형성하기 위한 것입니다(그림 10 및 표 3). 논리 값 "true"는 디지털 단위에 해당하고 "false"는 XNUMX에 해당합니다.

쌀. 10. 비교 연산자의 구문

표 3. 비교 연산자

4. 논리 연산자는 표현식에서 비트 연산을 수행합니다(그림 11). 표현은 절대적이어야 합니다. 즉, 번역자가 숫자 값을 계산할 수 있어야 합니다.

쌀. 11. 논리 연산자의 구문

5. 인덱스 연산자 []. 괄호도 연산자이며, 번역자는 이 괄호 뒤에 expression_1 값을 괄호로 묶은 expression_2를 추가하라는 명령으로 그 존재를 인식합니다(그림 12).

쌀. 12. 인덱스 연산자 구문

다음 지정은 어셈블러에 관한 문헌에서 채택됩니다. 텍스트가 레지스터의 내용을 참조할 때 해당 이름은 괄호 안에 표시됩니다. 우리는 또한 이 표기법을 고수할 것입니다.

6. ptr 유형 재정의 연산자는 표현식으로 정의된 레이블 또는 변수의 유형을 재정의하거나 한정하는 데 사용됩니다(그림 13).

유형은 byte, word, dword, qword, tbyte, near, far 값 중 하나를 사용할 수 있습니다.

쌀. 13. 타입 재정의 연산자 구문

7. 세그먼트 재정의 연산자 ":"(콜론)은 특정 세그먼트 구성요소("세그먼트 레지스터 이름", 해당 SEGMENT 지시문의 "세그먼트 이름" 또는 "그룹 이름")와 관련된 물리적 주소의 계산을 강제합니다. 14). 세분화에 대해 논의할 때 하드웨어 수준의 마이크로프로세서가 코드, 스택 및 데이터의 세 가지 유형의 세그먼트를 지원한다는 사실에 대해 이야기했습니다. 이 하드웨어 지원은 무엇입니까? 예를 들어, 다음 명령의 실행을 선택하기 위해 마이크로프로세서는 반드시 세그먼트 레지스터 cs의 내용을 보고 그것만 봐야 합니다. 그리고 우리가 알고 있듯이 이 레지스터에는 명령 세그먼트 시작의 물리적 주소(아직 이동되지 않은)가 포함되어 있습니다. 특정 명령의 주소를 얻으려면 마이크로프로세서가 cs의 내용에 16을 곱하고(20비트 이동을 의미함) 결과 16비트 값을 ip 레지스터의 XNUMX비트 내용에 더해야 합니다. 마이크로프로세서가 기계 명령어의 피연산자를 처리할 때도 거의 같은 일이 발생합니다. 피연산자가 주소(물리적 주소의 일부인 유효 주소)인 경우 찾을 세그먼트를 알 수 있습니다. 기본적으로 시작 주소가 세그먼트 레지스터 ds에 저장되어 있는 세그먼트입니다. .

그러나 스택 세그먼트는 어떻습니까? 고려하는 맥락에서 우리는 sp 및 bp 레지스터에 관심이 있습니다. 마이크로프로세서가 이러한 레지스터 중 하나를 피연산자(또는 피연산자가 식인 경우 그 일부)로 인식하면 기본적으로 ss 레지스터의 내용을 세그먼트 구성 요소로 사용하여 피연산자의 물리적 주소를 구성합니다. 이것은 마이크로프로세서 기계 명령 시스템의 명령 중 하나를 실행하는 마이크로 프로그램 제어 장치의 마이크로 프로그램 세트입니다. 각 마이크로 프로그램은 자체 알고리즘에 따라 작동합니다. 물론 변경할 수는 없지만 약간 수정할 수는 있습니다. 이는 선택적 기계 명령 접두사 필드를 사용하여 수행됩니다. 명령 작동 방식에 동의하면 이 필드가 누락된 것입니다. 명령 알고리즘을 수정하려면 (물론 특정 명령에 대해 허용되는 경우) 적절한 접두사를 형성해야합니다.

접두사는 숫자 값이 용도를 결정하는 XNUMX바이트 값입니다. 마이크로 프로세서는 지정된 값으로 이 바이트가 접두어임을 인식하고 마이크로 프로그램의 추가 작업은 수신된 명령을 고려하여 작업을 수정하도록 수행됩니다. 이제 우리는 그 중 하나인 세그먼트 교체(재정의) 접두사에 관심이 있습니다. 그 목적은 우리가 기본 세그먼트를 사용하고 싶지 않다는 것을 마이크로프로세서(사실상 펌웨어)에 알리는 것입니다. 물론 그러한 재정의의 가능성은 제한적입니다. 명령 세그먼트는 재정의할 수 없으며 다음 실행 명령의 주소는 cs: ip 쌍에 의해 고유하게 결정됩니다. 그리고 여기 스택과 데이터의 세그먼트가 가능합니다. 이것이 ":" 연산자의 목적입니다. 이 명령문을 처리하는 어셈블러 변환기는 해당하는 XNUMX바이트 세그먼트 대체 접두어를 생성합니다.

쌀. 14. 세그먼트 재정의 연산자의 구문

8. 구조 유형 명명 연산자 "."(도트)는 표현식에서 발생하는 경우 컴파일러가 특정 계산을 수행하도록 합니다.

9. 표현식 seg의 주소의 세그먼트 구성 요소를 얻기 위한 연산자는 레이블, 변수, 세그먼트 이름, 그룹 이름 또는 일부 기호 이름이 될 수 있는 표현식(그림 15)에 대한 세그먼트의 물리적 주소를 반환합니다. .

쌀. 15. 세그먼트 컴포넌트 수신 연산자의 구문

10. 표현식 오프셋의 오프셋을 얻기 위한 연산자를 사용하면 표현식이 정의된 세그먼트의 시작 부분에 상대적인 바이트 단위의 표현식 오프셋 값(그림 16)을 얻을 수 있습니다.

쌀. 16. 오프셋 가져오기 연산자의 구문

고급 언어에서와 마찬가지로 식을 평가할 때 어셈블러 연산자의 실행은 우선 순위에 따라 수행됩니다(표 4). 우선 순위가 같은 작업은 왼쪽에서 오른쪽으로 순차적으로 실행됩니다. 우선 순위가 가장 높은 괄호를 배치하여 실행 순서를 변경할 수 있습니다.

표 4. 연산자 및 우선 순위

3. 분할 지시문

이전 논의 과정에서 우리는 어셈블리 언어 프로그램에서 명령과 피연산자를 작성하기 위한 모든 기본 규칙을 발견했습니다. 변환기가 명령을 처리하고 마이크로프로세서가 명령을 실행할 수 있도록 명령 시퀀스를 적절하게 형식화하는 방법에 대한 질문은 여전히 ​​열려 있습니다.

마이크로프로세서의 아키텍처를 고려할 때 우리는 동시에 작동할 수 있는 XNUMX개의 세그먼트 레지스터가 있음을 알게 되었습니다.

1) 하나의 코드 세그먼트로;

2) 하나의 스택 세그먼트로;

3) 하나의 데이터 세그먼트로;

4) XNUMX개의 추가 데이터 세그먼트 포함.

세그먼트는 해당 세그먼트 레지스터의 값을 기준으로 주소가 계산되는 명령 및/또는 데이터가 물리적으로 차지하는 메모리 영역임을 다시 한 번 상기하십시오.

어셈블러 세그먼트의 구문 설명은 그림 17에 표시된 구조입니다.

쌀. 17. 세그먼트 설명 구문

세그먼트의 기능은 단순히 프로그램을 코드 블록, 데이터 및 스택으로 나누는 것보다 다소 광범위하다는 점에 유의해야 합니다. 분할은 모듈식 프로그래밍의 개념과 관련된 보다 일반적인 메커니즘의 일부입니다. 여기에는 다른 프로그래밍 언어의 모듈을 포함하여 컴파일러에서 만든 개체 모듈 디자인의 통합이 포함됩니다. 이를 통해 서로 다른 언어로 작성된 프로그램을 결합할 수 있습니다. SEGMENT 지시문의 피연산자가 의도하는 것은 그러한 조합에 대한 다양한 옵션의 구현을 위한 것입니다.

그들을 더 자세히 고려하십시오.

1. 세그먼트 정렬 속성(정렬 유형)은 세그먼트의 시작이 지정된 경계에 배치되도록 링커에 지시합니다. 적절하게 정렬하면 i80x86 프로세서에서 데이터 액세스 속도가 빨라지기 때문에 이는 중요합니다. 이 속성의 유효한 값은 다음과 같습니다.

1) BYTE - 정렬이 수행되지 않습니다. 세그먼트는 모든 메모리 주소에서 시작할 수 있습니다.

2) WORD - 세그먼트는 0의 배수인 주소에서 시작합니다. 즉, 물리적 주소의 마지막(최하위) 비트는 XNUMX(단어 경계에 맞춰 정렬됨)입니다.

3) DWORD - 세그먼트는 0의 배수인 주소에서 시작합니다. 즉, 마지막 XNUMX개(최하위) 비트는 XNUMX(이중 단어 경계 정렬)입니다.

4) PARA - 세그먼트는 16의 배수인 주소에서 시작합니다. 즉, 주소의 마지막 XNUMX진수는 Oh(단락 경계에 정렬)여야 합니다.

5) PAGE - 세그먼트는 256의 배수인 주소에서 시작합니다. 즉, 마지막 00개의 256진수 숫자는 XNUMXh여야 합니다(XNUMX바이트 페이지의 경계에 정렬됨).

6) MEMPAGE - 세그먼트는 4KB의 배수인 주소에서 시작합니다. 즉, 마지막 4자리 XNUMX진수는 OOOh(다음 XNUMXKB 메모리 페이지의 주소)여야 합니다. 기본 정렬 유형은 PARA입니다.

2. 결합 세그먼트 속성(조합 유형)은 이름이 같은 서로 다른 모듈의 세그먼트를 결합하는 방법을 링커에 알려줍니다. 세그먼트 조합 속성 값은 다음과 같습니다.

1) PRIVATE - 세그먼트는 이 모듈 외부의 동일한 이름을 가진 다른 세그먼트와 병합되지 않습니다.

2) PUBLIC - 링커가 동일한 이름을 가진 모든 세그먼트를 연결하도록 합니다. 새로 병합된 세그먼트는 완전하고 연속적입니다. 개체의 모든 주소(오프셋)는 명령 및 데이터 세그먼트의 유형에 따라 달라질 수 있으며 이 새 세그먼트의 시작 부분을 기준으로 계산됩니다.

3) COMMON - 동일한 주소에 동일한 이름을 가진 모든 세그먼트를 배치합니다. 주어진 이름을 가진 모든 세그먼트는 겹치고 메모리를 공유합니다. 결과 세그먼트의 크기는 가장 큰 세그먼트의 크기와 같습니다.

4) AT xxxx - 단락의 절대 주소에 세그먼트를 찾습니다(단락은 메모리의 양, 16의 배수이므로 단락 주소의 마지막 0진수는 XNUMX입니다). 단락의 절대 주소는 xxx로 지정됩니다. 링커는 결합 속성을 고려하여 주어진 메모리 주소(예를 들어 비디오 메모리 또는 ROM> 영역에 액세스하는 데 사용할 수 있음)에 세그먼트를 배치합니다. 물리적으로 이것은 세그먼트가 메모리에 로드될 때 단락의 이 절대 주소에서 시작하여 위치하지만 액세스하려면 속성에 지정된 값을 해당 세그먼트 레지스터에 로드해야 함을 의미합니다. 이렇게 정의된 세그먼트의 모든 레이블과 주소는 주어진 절대 주소에 상대적입니다.

5) 스택 - 스택 세그먼트의 정의. 링커가 동일한 이름을 가진 모든 세그먼트를 연결하고 ss 레지스터를 기준으로 이러한 세그먼트의 주소를 계산하도록 합니다. 결합된 유형 STACK(스택)은 ss 레지스터가 스택 세그먼트에 대한 표준 세그먼트 레지스터라는 점을 제외하면 결합된 유형 PUBLIC과 유사합니다. sp 레지스터는 연결된 스택 세그먼트의 끝으로 설정됩니다. 스택 세그먼트가 지정되지 않은 경우 링커는 스택 세그먼트를 찾을 수 없다는 경고를 발행합니다. 스택 세그먼트가 생성되고 결합된 스택 유형이 사용되지 않는 경우 프로그래머는 세그먼트 주소를 ss 레지스터(ds 레지스터와 유사)에 명시적으로 로드해야 합니다.

조합 속성의 기본값은 PRIVATE입니다.

3. 세그먼트 클래스 속성(클래스 유형)은 링커가 여러 모듈 세그먼트에서 프로그램을 조립할 때 적절한 세그먼트 순서를 결정하는 데 도움이 되는 인용 문자열입니다. 링커는 동일한 클래스 이름을 가진 모든 세그먼트를 메모리에서 함께 결합합니다(클래스 이름은 일반적으로 무엇이든 될 수 있지만 세그먼트의 기능을 반영하는 경우 더 좋습니다). 클래스 이름의 일반적인 사용은 프로그램의 모든 코드 세그먼트를 함께 그룹화하는 것입니다(일반적으로 "코드" 클래스가 이를 위해 사용됨). 클래스 유형 지정 메커니즘을 사용하여 초기화된 데이터 세그먼트와 초기화되지 않은 데이터 세그먼트를 그룹화할 수도 있습니다.

4. 세그먼트 크기 속성. i80386 이상 프로세서의 경우 세그먼트는 16비트 또는 32비트일 수 있습니다. 이것은 주로 세그먼트의 크기와 그 안에 형성되는 물리적 주소의 순서에 영향을 미칩니다. 속성은 다음 값을 가질 수 있습니다.

1) USE16 - 세그먼트가 16비트 주소 지정을 허용함을 의미합니다. 물리적 주소를 구성할 때 16비트 오프셋만 사용할 수 있습니다. 따라서 이러한 세그먼트는 최대 64KB의 코드 또는 데이터를 포함할 수 있습니다.

2)USE32 - 세그먼트가 32비트가 됩니다. 물리적 주소를 구성할 때 32비트 오프셋을 사용할 수 있습니다. 따라서 이러한 세그먼트는 최대 4GB의 코드 또는 데이터를 포함할 수 있습니다.

SEGMENT 및 ENDS 지시문에는 세그먼트의 기능적 목적에 대한 정보가 포함되어 있지 않기 때문에 모든 세그먼트는 그 자체로 동일합니다. 그것들을 코드, 데이터 또는 스택 세그먼트로 사용하려면 먼저 번역자에게 이에 대해 알려야 하며, 이를 위해 그림 18에 표시된 형식의 특수 ASSUME 지시문이 사용됩니다. 18. 이 지시어는 어떤 세그먼트가 어떤 세그먼트 레지스터에 바인딩되어 있는지 변환기에게 알려줍니다. 이렇게 하면 번역자가 세그먼트에 정의된 기호 이름을 올바르게 바인딩할 수 있습니다. 세그먼트 레지스터에 대한 세그먼트 바인딩은 이 지시문의 피연산자를 사용하여 수행되며 여기서 segment_name은 SEGMENT 지시문 또는 nothing 키워드에 의해 프로그램의 소스 코드에 정의된 세그먼트의 이름이어야 합니다. 키워드 nothing만 피연산자로 사용되는 경우 이전 세그먼트 레지스터 할당이 취소되고 한 번에 XNUMX개 세그먼트 레지스터 모두에 대해 취소됩니다. 그러나 세그먼트 이름 인수 대신 키워드 nothing을 사용할 수 있습니다. 이 경우 이름이 세그먼트 이름인 세그먼트와 해당 세그먼트 레지스터 간의 연결이 선택적으로 끊어집니다(그림 XNUMX 참조).

쌀. 18. ASSUME 지시어

코드, 데이터 및 스택에 대한 하나의 세그먼트를 포함하는 간단한 프로그램의 경우 설명을 단순화하고 싶습니다. 이를 위해 번역자 MASM 및 TASM은 단순화된 분할 지시문을 사용하는 기능을 도입했습니다. 그러나 여기서 세그먼트의 배치와 조합을 직접 제어할 수 없는 것을 어떻게든 보상해야 한다는 문제가 발생했습니다. 이를 위해 단순화된 분할 지시어와 함께 부분적으로 세그먼트 배치를 제어하고 ASSUME 지시어의 기능을 수행하기 시작한 MODEL 메모리 모델을 지정하는 지시어를 사용하기 시작했습니다(따라서 단순화된 분할 지시어를 사용할 때 ASSUME 지시문은 생략 가능). 이 지시어는 단순화된 분할 지시어를 사용하는 경우 미리 정의된 이름이 있는 세그먼트를 세그먼트 레지스터와 바인딩합니다(여전히 ds를 명시적으로 초기화해야 함).

MODEL 지시문의 구문은 그림 19에 나와 있습니다.

쌀. 19. MODEL 지시문의 구문

MODEL 지시문의 필수 매개변수는 메모리 모델입니다. 이 매개변수는 POU의 메모리 분할 모델을 정의합니다. 프로그램 모듈은 앞에서 언급한 단순화된 세그먼트 설명 지시문에 의해 정의된 특정 유형의 세그먼트만 가질 수 있다고 가정합니다. 이러한 지시문은 표 5에 나와 있습니다.

표 5. 간소화된 세그먼트 정의 지시문

일부 지시문에 [name] 매개변수가 있다는 것은 이 유형의 여러 세그먼트를 정의할 수 있음을 나타냅니다. 반면에 여러 종류의 데이터 세그먼트가 존재하는 것은 초기화된 데이터와 초기화되지 않은 데이터 및 상수에 대해 서로 다른 데이터 세그먼트를 생성하는 고급 언어의 일부 컴파일러와의 호환성을 보장해야 하는 요구 사항 때문입니다.

MODEL 지시문을 사용할 때 컴파일러는 주어진 메모리 모델의 특정 특성에 대한 정보를 얻기 위해 프로그램 작동 중에 액세스할 수 있는 여러 식별자를 사용할 수 있도록 합니다(표 7). 이러한 식별자와 해당 값을 나열해 보겠습니다(표 6).

표 6. MODEL 지시문에 의해 생성된 식별자

이제 MODEL 지시어에 대한 논의를 마칠 수 있습니다. MODEL 지시어의 피연산자는 프로그램 세그먼트 집합, 데이터 및 코드 세그먼트의 크기, 세그먼트와 세그먼트 레지스터를 연결하는 방법을 정의하는 메모리 모델을 지정하는 데 사용됩니다. 표 7은 MODEL 지시문의 "메모리 모델" 매개변수의 일부 값을 보여줍니다.

표 7. 메모리 모델

MODEL 지시문의 "modifier" 매개변수를 사용하면 선택한 메모리 모델을 사용하는 일부 기능을 지정할 수 있습니다(표 8).

표 8. 메모리 모델 수정자

선택적 매개변수 "언어" 및 "언어 수정자"는 프로시저 호출의 일부 기능을 정의합니다. 이러한 매개변수를 사용해야 할 필요성은 다양한 프로그래밍 언어로 프로그램을 작성하고 링크할 때 발생합니다.

우리가 설명한 표준 및 간소화된 세분화 지시문은 상호 배타적이지 않습니다. 표준 지시문은 프로그래머가 메모리의 세그먼트 배치 및 다른 모듈의 세그먼트와의 조합을 완전히 제어하고자 할 때 사용됩니다.

단순화된 지시문은 간단한 프로그램 및 고급 언어로 작성된 프로그램 모듈과 연결하기 위한 프로그램에 유용합니다. 이를 통해 링커는 연결 및 관리를 표준화하여 서로 다른 언어의 모듈을 효율적으로 연결할 수 있습니다.

강의 17번. 어셈블러의 명령 구조

1. 기계 명령어 구조

기계 명령은 특정 규칙에 따라 인코딩된 마이크로프로세서에 대한 명령으로 일부 작업이나 작업을 수행합니다. 각 명령에는 다음을 정의하는 요소가 포함되어 있습니다.

1) 무엇을 할 것인가? (이 질문에 대한 답은 연산 코드(COP)라는 명령 요소에 의해 제공됩니다.);

2) 무언가를 수행해야 하는 객체(이 요소를 피연산자라고 함)

3) 어떻게 할 것인가? (이러한 요소를 피연산자 유형이라고 하며 일반적으로 암시적으로 지정됩니다.)

그림 20에 표시된 기계 명령어 형식이 가장 일반적입니다. 기계 명령어의 최대 길이는 15바이트입니다. 실제 명령에는 훨씬 적은 수의 필드, 최대 하나의 KOP만 포함될 수 있습니다.

쌀. 20. 기계 명령어 형식

기계 명령 필드의 목적을 설명하겠습니다.

1. 접두사.

각각 1바이트이거나 생략될 수 있는 선택적 기계 명령 요소입니다. 메모리에서 접두사는 명령보다 우선합니다. 접두사의 목적은 명령이 수행하는 작업을 수정하는 것입니다. 애플리케이션은 다음 유형의 접두사를 사용할 수 있습니다.

1) 세그먼트 교체 프리픽스. 스택 또는 데이터 주소를 지정하기 위해 이 명령어에서 사용되는 세그먼트 레지스터를 명시적으로 지정합니다. 접두사는 기본 세그먼트 레지스터 선택을 재정의합니다. 세그먼트 교체 접두사의 의미는 다음과 같습니다.

a) 2eh - 세그먼트 cs 교체;

b) 36h - 세그먼트 ss 교체;

c) 3eh - 세그먼트 ds 교체;

d) 26h - 세그먼트 es 교체;

e) 64h - 세그먼트 fs 교체;

e) 65h - 세그먼트 gs 교체;

2) 주소 비트 수 접두사는 주소의 비트 수(32비트 또는 16비트)를 지정합니다. 주소 피연산자를 사용하는 각 명령어에는 해당 피연산자 주소의 비트 폭이 할당됩니다. 이 주소는 16비트 또는 32비트일 수 있습니다. 이 명령의 주소 폭이 16비트인 경우 이는 명령에 16비트 오프셋이 포함되어 있음을 의미하며(그림 20) 일부 세그먼트의 시작 부분에 상대적인 주소 피연산자의 16비트 오프셋에 해당합니다. 그림 21과 관련하여 이 오프셋을 유효 주소라고 합니다. 주소가 32비트인 경우 이는 명령에 32비트 오프셋이 포함되어 있음을 의미하며(그림 20) 세그먼트 시작 부분에 대한 주소 피연산자의 32비트 오프셋에 해당하며 그 값은 32를 형성합니다. -세그먼트의 비트 오프셋. 주소 비트 수 접두사는 기본 주소 비트 수를 변경하는 데 사용할 수 있습니다. 이 변경은 접두사가 앞에 오는 명령에만 영향을 미칩니다.

쌀. 21. 리얼 모드에서 물리적 주소 형성 메커니즘

3) 피연산자 비트 폭 접두사는 주소 비트 폭 접두사와 유사하지만 명령어가 동작하는 피연산자 비트 길이(32비트 또는 16비트)를 나타냅니다. 기본적으로 주소 및 피연산자 비트 폭 속성을 설정하는 규칙은 무엇입니까?

리얼 모드와 가상 18086 모드에서 이러한 속성의 값은 16비트입니다. 보호 모드에서 속성 값은 실행 가능한 세그먼트 디스크립터의 D 비트 상태에 따라 다릅니다. D = 0이면 기본 속성 값은 16비트입니다. D = 1이면 32비트입니다.

피연산자 너비 66h 및 주소 너비 67h에 대한 접두사 값. 리얼 모드 주소 비트 접두사를 사용하면 32비트 주소 지정을 사용할 수 있지만 64KB 세그먼트 크기 제한에 유의하십시오. 주소 너비 접두사와 유사하게 리얼 모드 피연산자 너비 접두사를 사용하여 32비트 피연산자(예: 산술 명령어)로 작업할 수 있습니다.

4) 반복 접두사는 체인 명령(라인 처리 명령)과 함께 사용됩니다. 이 접두사는 체인의 모든 요소를 ​​처리하는 명령을 "반복"합니다. 명령 시스템은 두 가지 유형의 접두사를 지원합니다.

a) 무조건(rep - OOh), 연결된 명령이 특정 횟수만큼 반복되도록 강제합니다.

b) 조건부(repe/repz - OOh, repne/repnz - 0f2h), 루핑 시 일부 플래그를 확인하고 확인 결과 루프에서 조기 종료가 가능합니다.

2. 작업 코드.

명령에 의해 수행되는 작업을 설명하는 필수 요소입니다. 많은 명령이 여러 작업 코드에 해당하며 각 코드는 작업의 뉘앙스를 결정합니다. 기계 명령어의 후속 필드는 작업과 관련된 피연산자의 위치와 사용 세부 사항을 결정합니다. 이러한 필드에 대한 고려는 기계 명령어에서 피연산자를 지정하는 방법과 연결되므로 나중에 수행됩니다.

3. 주소 지정 모드 바이트 modr/m.

이 바이트의 값은 사용되는 피연산자 주소 형식을 결정합니다. 피연산자는 하나 또는 두 개의 레지스터에 있는 메모리에 있을 수 있습니다. 피연산자가 메모리에 있는 경우 modr/m 바이트는 유효 주소를 계산하는 데 사용되는 구성 요소(오프셋, 기본 및 인덱스 레지스터)를 지정합니다(그림 21). 보호 모드에서는 sib 바이트(Scale-Index-Base)를 추가로 사용하여 메모리에서 피연산자의 위치를 ​​결정할 수 있습니다. modr/m 바이트는 세 개의 필드로 구성됩니다(그림 20).

1) mod 필드는 명령에서 피연산자 주소가 차지하는 바이트 수를 결정합니다(그림 20, 명령의 오프셋 필드). mod 필드는 "명령 오프셋" 피연산자의 주소를 수정하는 방법을 지정하는 r/m 필드와 함께 사용됩니다. 예를 들어 mod = 00이면 명령에 오프셋 필드가 없고 피연산자의 주소가 기본 및(또는) 인덱스 레지스터의 내용에 의해 결정됨을 의미합니다. 유효 주소를 계산하는 데 사용할 레지스터는 이 바이트의 값에 따라 결정됩니다. mod = 01이면 오프셋 필드가 명령에 존재하고 1바이트를 차지하며 기본 및(또는) 인덱스 레지스터의 내용에 의해 수정됨을 의미합니다. mod = 10이면 오프셋 필드가 명령에 있고 2바이트 또는 4바이트를 차지하며(기본 또는 접두사 주소 크기에 따라 다름) 기본 및/또는 인덱스 레지스터의 내용에 의해 수정됨을 의미합니다. mod = 11이면 메모리에 피연산자가 없고 레지스터에 있음을 의미합니다. 명령에서 즉시 피연산자가 사용될 때 mod 바이트의 동일한 값이 사용됩니다.

2) reg/cop 필드는 첫 번째 피연산자 대신 명령에 위치한 레지스터 또는 opcode의 가능한 확장을 결정합니다.

3) r/m 필드는 mod 필드와 함께 사용되며 명령에서 첫 번째 피연산자의 위치에 있는 레지스터(mod = 11인 경우) 또는 유효 주소를 계산하는 데 사용되는 기본 및 인덱스 레지스터를 결정합니다. (명령의 오프셋 필드와 함께).

4. 바이트 스케일 - 인덱스 - 베이스(바이트 sib).

피연산자 주소 지정 가능성을 확장하는 데 사용됩니다. 기계 명령어에서 sib 바이트의 존재는 mod 필드의 값 01 또는 10 중 하나와 r/m = 100 필드의 값의 조합으로 표시됩니다. sib 바이트는 세 개의 필드로 구성됩니다.

1) 스케일 필드 ss. 이 필드는 sib 바이트의 다음 3비트를 차지하는 인덱스 구성 요소 인덱스에 대한 배율 인수를 포함합니다. ss 필드는 1, 2, 4, 8 값 중 하나를 포함할 수 있습니다.

유효 주소를 계산할 때 인덱스 레지스터의 내용에 이 값을 곱합니다.

2) 인덱스 필드. 피연산자의 유효 주소를 계산하는 데 사용되는 인덱스 레지스터 번호를 저장하는 데 사용됩니다.

3) 기본 필드. 피연산자의 유효 주소를 계산하는 데에도 사용되는 기본 레지스터 번호를 저장하는 데 사용됩니다. 거의 모든 범용 레지스터는 기본 및 인덱스 레지스터로 사용할 수 있습니다.

5. 명령의 오프셋 필드.

피연산자의 유효 주소 값을 전체적으로 또는 부분적으로(위 고려 사항에 따름) 나타내는 8, 16 또는 32비트 부호 있는 정수입니다.

6. 직접 피연산자의 필드.

8비트, 16비트 또는 32비트 즉치 피연산자인 선택적 필드입니다. 물론 이 필드의 존재는 modr/m 바이트의 값에 반영됩니다.

2. 명령어 피연산자를 지정하는 방법

피연산자는 펌웨어 수준에서 암시적으로 설정됩니다.

이 경우 명령어에는 피연산자가 명시적으로 포함되어 있지 않습니다. 명령 실행 알고리즘은 일부 기본 개체(레지스터, 플래그의 플래그 등)를 사용합니다.

예를 들어 cli 및 sti 명령은 암시적으로 eflags 레지스터의 if 인터럽트 플래그와 함께 작동하고 xlat 명령은 암시적으로 al 레지스터와 ds:bx 레지스터 쌍으로 지정된 주소에서 메모리의 라인에 액세스합니다.

피연산자는 명령어 자체에 지정됩니다(즉시 피연산자).

피연산자는 명령어 코드에 있습니다. 즉, 명령어의 일부입니다. 이러한 피연산자를 명령에 저장하기 위해 최대 32비트 길이의 필드가 할당됩니다(그림 20). 직접 피연산자는 두 번째(소스) 피연산자만 될 수 있습니다. 대상 피연산자는 메모리 또는 레지스터에 있을 수 있습니다.

예: mov ax,0ffffti는 2진수 상수 ffff를 레지스터 ax로 이동합니다. add sum, 2 명령은 정수 XNUMX의 합계 주소에 있는 필드의 내용을 더하고 그 결과를 첫 번째 피연산자의 위치, 즉 메모리에 씁니다.

피연산자가 레지스터 중 하나에 있음

레지스터 피연산자는 레지스터 이름으로 지정됩니다. 레지스터를 사용할 수 있습니다.

1) 32비트 레지스터 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EUR;

2) 16비트 레지스터 AX, BX, CX, DX, SI, DI, SP, BP;

3) 8비트 레지스터 AH, AL, BH, BL, CH, CL, DH, DL;

4) 세그먼트 레지스터 CS, DS, SS, ES, FS, GS.

예를 들어 add ax,bx 명령은 레지스터 ax 및 bx의 내용을 추가하고 결과를 bx에 기록합니다. dec si 명령은 si의 내용을 1씩 감소시킵니다.

피연산자가 메모리에 있음

이것은 피연산자를 지정하는 가장 복잡하고 동시에 가장 유연한 방법입니다. 직접 및 간접의 두 가지 주요 주소 지정 유형을 구현할 수 있습니다.

차례로 간접 주소 지정에는 다음과 같은 종류가 있습니다.

1) 간접 기본 주소 지정 다른 이름은 레지스터 간접 주소 지정입니다.

2) 오프셋을 사용한 간접 기본 주소 지정

3) 오프셋을 사용한 간접 인덱스 주소 지정

4) 간접 기본 인덱스 주소 지정;

5) 오프셋을 사용한 간접 기본 인덱스 주소 지정.

피연산자는 I/O 포트입니다.

RAM 주소 공간 외에도 마이크로프로세서는 I/O 장치에 액세스하는 데 사용되는 I/O 주소 공간을 유지 관리합니다. I/O 주소 공간은 64KB입니다. 주소는 이 공간의 모든 컴퓨터 장치에 할당됩니다. 이 공간 내의 특정 주소 값을 I/O 포트라고 합니다. 물리적으로 I/O 포트는 하드웨어 레지스터(마이크로프로세서 레지스터와 혼동하지 말 것)에 해당하며 특수 어셈블러 명령어를 사용하여 액세스합니다.

예를 들면 다음과 같습니다

al,60h; 포트 60h에서 바이트를 입력하십시오

I/O 포트에 의해 주소가 지정된 레지스터는 8,16, 32 또는 XNUMX비트 폭일 수 있지만 레지스터 비트 폭은 특정 포트에 대해 고정되어 있습니다. in 및 out 명령은 고정된 개체 범위에서 작동합니다. 소위 누산기 레지스터 EAX, AX, AL은 정보 소스 또는 수신자로 사용됩니다. 레지스터의 선택은 포트의 비트에 의해 결정됩니다. 포트 번호는 in 및 out 명령어의 직접 피연산자 또는 DX 레지스터의 값으로 지정할 수 있습니다. 마지막 방법을 사용하면 프로그램에서 포트 번호를 동적으로 결정할 수 있습니다.

피연산자는 스택에 있습니다.

명령어에는 피연산자가 전혀 없을 수도 있고, 하나 또는 두 개의 피연산자가 있을 수도 있습니다. 대부분의 명령어에는 두 개의 피연산자가 필요하며 그 중 하나는 소스 피연산자이고 다른 하나는 대상 피연산자입니다. 하나의 피연산자는 레지스터나 메모리에 위치할 수 있고 두 번째 피연산자는 레지스터에 있거나 명령어에 직접 있어야 한다는 것이 중요합니다. 직접 피연산자는 소스 피연산자만 될 수 있습니다. 두 개의 피연산자 기계 명령어에서 다음과 같은 피연산자의 조합이 가능합니다.

1) 등록 - 등록;

2) 레지스터 - 메모리;

3) 메모리 - 레지스터;

4) 즉각적인 피연산자 - 레지스터

5) 즉각적인 피연산자 - 메모리.

다음과 관련하여 이 규칙에는 예외가 있습니다.

1) 메모리에서 메모리로 데이터를 이동할 수 있는 체인 명령

2) 메모리에서 역시 메모리에 있는 스택으로 데이터를 전송할 수 있는 스택 명령;

3) 명령에 지정된 피연산자 외에도 두 번째 암시적 피연산자를 사용하는 곱셈 유형의 명령.

나열된 피연산자 조합 중에서 레지스터-메모리 및 메모리-레지스터가 가장 자주 사용됩니다. 그것들의 중요성을 고려하여 우리는 그것들을 더 자세히 고려할 것입니다. 하나 또는 다른 유형의 주소 지정이 적용될 때 어셈블러 명령의 형식이 어떻게 변경되는지 보여주는 어셈블러 명령의 예와 함께 토론에 동행할 것입니다. 이와 관련하여 마이크로 프로세서의 주소 버스에 물리적 주소를 형성하는 원리를 보여주는 그림 21을 다시 살펴보십시오. 피연산자의 주소는 4비트만큼 이동된 세그먼트 레지스터의 내용과 일반적으로 다음 세 가지 구성 요소의 합으로 계산되는 16비트 유효 주소의 두 구성 요소의 합으로 형성됨을 알 수 있습니다. 오프셋 및 인덱스.

3. 주소 지정 방법

우리는 메모리에 주소 지정 피연산자의 주요 유형의 기능을 나열하고 고려합니다.

1) 직접 주소 지정

2) 간접 기본(레지스터) 주소 지정;

3) 오프셋을 사용한 간접 기본(레지스터) 주소 지정;

4) 오프셋을 사용한 간접 인덱스 주소 지정

5) 간접 기본 인덱스 주소 지정;

6) 오프셋을 사용한 간접 기본 인덱스 주소 지정.

직접 주소 지정

이는 메모리에서 피연산자를 주소 지정하는 가장 간단한 형태입니다. 유효 주소는 명령어 자체에 포함되어 있고 추가 소스나 레지스터를 형성하는 데 사용되지 않기 때문입니다. 유효 주소는 크기가 20, 8, 16비트일 수 있는 기계 명령어 오프셋 필드(그림 32 참조)에서 직접 가져옵니다. 이 값은 데이터 세그먼트에 있는 바이트, 워드 또는 더블 워드를 고유하게 식별합니다.

직접 주소 지정에는 두 가지 유형이 있습니다.

상대 직접 주소 지정

상대 점프 주소를 나타내는 조건부 점프 명령에 사용됩니다. 이러한 전환의 상대성은 기계 명령어의 오프셋 필드에 8비트, 16비트 또는 32비트 값이 포함되어 있다는 사실에 있습니다. ip/eip 명령어 포인터 레지스터 이 추가의 결과로 전환이 수행되는 주소가 얻어집니다.

절대 직접 주소 지정

이 경우 유효 주소는 기계 명령어의 일부이지만 이 주소는 명령어의 오프셋 필드 값으로만 ​​구성됩니다. 메모리에서 피연산자의 물리적 주소를 형성하기 위해 마이크로프로세서는 4비트만큼 이동된 세그먼트 레지스터 값과 함께 이 필드를 추가합니다. 이 주소 지정의 여러 형식을 어셈블러 명령어에서 사용할 수 있습니다.

그러나 이러한 주소 지정은 거의 사용되지 않습니다. 프로그램에서 일반적으로 사용되는 셀에는 기호 이름이 지정됩니다. 변환하는 동안 어셈블러는 이러한 이름의 오프셋 값을 계산하여 "명령 오프셋" 필드에서 생성하는 기계 명령어로 대체합니다. 결과적으로 기계 명령어는 피연산자에 직접 주소를 지정하고 실제로 해당 필드 중 하나에 유효 주소 값을 가지고 있음이 밝혀졌습니다.

다른 유형의 주소 지정은 간접적입니다. 이러한 유형의 주소 지정 이름에서 "간접"이라는 단어는 유효 주소의 일부만 명령어 자체에 있을 수 있고 나머지 구성 요소는 레지스터에 있으며 해당 내용은 modr/m 바이트로 표시되며, 아마도 sib 바이트로.

간접 기본(레지스터) 주소 지정

이 주소 지정을 사용하면 피연산자의 유효 주소는 sp / esp 및 bp / ebp(스택 세그먼트 작업을 위한 특정 레지스터임)를 제외한 모든 범용 레지스터에 있을 수 있습니다. 명령에서 구문적으로 이 주소 지정 모드는 레지스터 이름을 대괄호[]로 묶어 표현합니다. 예를 들어, 명령 mov ax, [ecx]는 레지스터 esx에 저장된 오프셋과 함께 데이터 세그먼트의 주소에 있는 단어의 내용을 레지스터 ax에 배치합니다. 레지스터의 내용은 프로그램이 진행되는 동안 쉽게 변경될 수 있으므로 이 주소 지정 방법을 사용하면 일부 기계 명령어에 대한 피연산자의 주소를 동적으로 할당할 수 있습니다. 이 속성은 예를 들어 순환 계산을 구성하고 테이블이나 배열과 같은 다양한 데이터 구조로 작업하는 데 매우 유용합니다.

오프셋을 사용한 간접 기본(레지스터) 주소 지정

이러한 유형의 주소 지정은 이전 주소 지정에 추가된 것으로 일부 기본 주소에 상대적인 알려진 오프셋을 사용하여 데이터에 액세스하도록 설계되었습니다. 이러한 유형의 주소 지정은 프로그램 개발 단계에서 요소의 오프셋을 미리 알고 구조의 기본(시작) 주소를 동적으로 계산해야 하는 경우 데이터 구조의 요소에 액세스하는 데 사용하기 편리합니다. 프로그램 실행 단계. 기본 레지스터의 내용을 수정하면 동일한 유형의 데이터 구조의 다른 인스턴스에서 동일한 이름의 요소에 액세스할 수 있습니다.

예를 들어 mov ax,[edx+3h] 명령은 메모리 영역에서 edx + 3h의 내용인 ax 레지스터로 워드를 전송합니다.

mov ax,mas[dx] 명령은 주소에 있는 ax 레지스터로 단어를 이동합니다. dx의 내용에 식별자 mas의 값을 더한 값입니다(컴파일러는 각 식별자에 이 식별자의 오프셋과 같은 값을 데이터 세그먼트의 시작).

오프셋을 사용한 간접 인덱스 주소 지정

이러한 종류의 주소 지정은 오프셋을 사용한 간접 기본 주소 지정과 매우 유사합니다. 여기서도 범용 레지스터 중 하나가 유효 주소를 형성하는 데 사용됩니다. 그러나 인덱스 주소 지정에는 배열 작업에 매우 편리한 한 가지 흥미로운 기능이 있습니다. 이는 소위 인덱스 레지스터 내용의 스케일링 가능성과 관련이 있습니다. 이게 뭐야?

그림 20을 보십시오. 우리는 sib 바이트에 관심이 있습니다. 이 바이트의 구조를 논의할 때 세 개의 필드로 구성되어 있음을 언급했습니다. 이러한 필드 중 하나는 인덱스 레지스터의 내용이 곱해지는 ss 스케일 필드입니다.

예를 들어 mov ax,mas[si*2] 명령에서 두 번째 피연산자의 유효 주소 값은 식 mas+(si)*2로 계산됩니다. 어셈블러에는 배열 인덱싱을 구성할 수단이 없기 때문에 프로그래머가 직접 구성해야 합니다.

확장 기능이 있으면 이 문제를 해결하는 데 크게 도움이 되지만 배열 요소의 크기가 1, 2, 4 또는 8바이트인 경우에 제공됩니다.

간접 기본 인덱스 주소 지정

이러한 유형의 주소 지정을 통해 유효 주소는 기본 및 색인이라는 두 개의 범용 레지스터 내용의 합으로 구성됩니다. 이러한 레지스터는 모든 범용 레지스터가 될 수 있으며 인덱스 레지스터 내용의 크기 조정이 자주 사용됩니다.

오프셋을 사용한 간접 기본 인덱스 주소 지정

이러한 종류의 주소 지정은 간접 인덱싱된 주소 지정의 보완입니다. 유효 주소는 기본 레지스터의 내용, 인덱스 레지스터의 내용, 명령의 오프셋 필드 값의 세 가지 구성 요소의 합으로 구성됩니다.

예를 들어, mov eax,[esi+5] [edx] 명령은 더블 워드를 (esi) + 5 + (edx) 주소의 eax 레지스터로 이동합니다.

add ax,array[esi] [ebx] 명령은 레지스터 ax의 내용을 주소에 있는 단어의 내용에 추가합니다: 식별자 배열의 값 + (esi) + (ebx).

강의 18번. 팀

1. 데이터 전송 명령

실제 적용의 편의와 특수성을 반영하기 위해 기능 목적에 따라 이 그룹의 명령을 고려하는 것이 더 편리하며, 이에 따라 다음 명령 그룹으로 나눌 수 있습니다.

1) 범용 데이터 전송

2) 포트에 대한 입출력;

3) 주소 및 포인터로 작업합니다.

4) 데이터 변환;

5) 스택 작업.

일반 데이터 전송 명령

이 그룹에는 다음 명령이 포함됩니다.

1) mov는 기본 데이터 전송 명령입니다. 다양한 배송 옵션을 구현합니다. 이 명령의 세부 사항에 유의하십시오.

a) mov 명령은 한 메모리 영역에서 다른 메모리 영역으로 전송하는 데 사용할 수 없습니다. 이러한 필요성이 발생하면 현재 사용 가능한 범용 레지스터를 중간 버퍼로 사용해야 합니다.

b) 메모리에서 세그먼트 레지스터로 직접 값을 로드하는 것은 불가능합니다. 따라서 이러한 로드를 수행하려면 중간 개체를 사용해야 합니다. 범용 레지스터 또는 스택일 수 있습니다.

c) 한 세그먼트 레지스터의 내용을 다른 세그먼트 레지스터로 전송할 수 없습니다. 이는 명령 시스템에 해당 opcode가 없기 때문입니다. 그러나 그러한 조치의 필요성이 종종 발생합니다. 중간 레지스터와 동일한 범용 레지스터를 사용하여 이러한 전송을 수행할 수 있습니다.

d) 대상 피연산자로 세그먼트 레지스터 CS를 사용할 수 없습니다. 그 이유는 간단합니다. 사실 마이크로프로세서의 아키텍처에서 cs: ip 쌍에는 항상 다음에 실행되어야 하는 명령의 주소가 포함되어 있습니다. mov 명령으로 CS 레지스터의 내용을 변경하는 것은 실제로 전송이 아니라 점프 작업을 의미하므로 허용되지 않습니다. 2) xchg - 양방향 데이터 전송에 사용됩니다. 물론이 작업을 위해 여러 mov 명령 시퀀스를 사용할 수 있지만 교환 작업이 자주 사용된다는 사실 때문에 마이크로 프로세서 명령 시스템 개발자는 별도의 xchg 교환 명령을 도입해야한다고 생각했습니다. 당연히 피연산자는 같은 유형이어야 합니다. 두 메모리 셀의 내용을 서로 교환하는 것은 (모든 어셈블러 명령어에 대해) 허용되지 않습니다.

포트 I/O 명령

그림 22를 보십시오. 컴퓨터 하드웨어 제어에 대한 매우 단순화된 개념 다이어그램을 보여줍니다.

쌀. 22. 컴퓨터 하드웨어 제어 개념도

그림 22에서 볼 수 있듯이 가장 낮은 수준은 하드웨어가 포트를 통해 직접 처리되는 BIOS 수준입니다. 이것은 장비 독립의 개념을 구현합니다. 하드웨어를 교체할 때 해당 BIOS 기능을 수정하고 새 주소와 포트 논리로 방향을 변경하기만 하면 됩니다.

기본적으로 포트를 통해 직접 장치를 관리하는 것은 쉽습니다. 포트 번호, 비트 깊이, 제어 정보 형식에 대한 정보는 장치의 기술 설명에 나와 있습니다. 행동의 궁극적인 목표, 특정 장치가 작동하는 알고리즘 및 포트 프로그래밍 순서만 알면 됩니다. 즉, 실제로 무엇을 어떤 순서로 보내야 하는지 알아야 합니다. 포트(기록할 때) 또는 포트에서 읽음(읽을 때) 및 이 정보를 해석하는 방법. 이를 위해서는 마이크로프로세서 명령 시스템에 있는 두 개의 명령만으로 충분합니다.

1) 누산기에서 port_number - port_number 번호가 있는 포트에서 누산기로의 입력;

2) 출력 포트, 누산기 - 누산기의 내용을 port_number 번호가 있는 포트로 출력합니다.

주소 및 메모리 포인터 작업을 위한 명령

어셈블러에서 프로그램을 작성할 때 메모리에 있는 피연산자의 주소로 집중적인 작업이 수행됩니다. 이러한 종류의 작업을 지원하기 위해 다음 명령을 포함하는 특수 명령 그룹이 있습니다.

1) lea 목적지, 소스 - 유효 주소 로딩;

2) ID 대상, 소스 - 포인터를 데이터 세그먼트 레지스터 ds로 로드합니다.

3) les destination, source - 추가 데이터 세그먼트 es의 레지스터에 포인터를 로드합니다.

4) lgs 목적지, 소스 - 추가 데이터 세그먼트 gs의 레지스터에 포인터를 로드합니다.

5) lfs 목적지, 소스 - 추가 데이터 세그먼트 fs의 레지스터에 포인터를 로드합니다.

6) lss 대상, 소스 - 포인터를 스택 세그먼트 레지스터 ss로 로드합니다.

lea 명령은 이동도 수행한다는 점에서 mov 명령과 유사합니다. 그러나 lea 명령은 데이터를 전송하지 않고 데이터의 유효 주소(즉, 데이터 세그먼트의 시작 부분에서 데이터의 오프셋)를 대상 피연산자가 나타내는 레지스터로 전송합니다.

종종 프로그램에서 어떤 작업을 수행하려면 유효 데이터 주소 값을 아는 것만으로는 충분하지 않고 데이터에 대한 전체 포인터가 필요합니다. 완전한 데이터 포인터는 세그먼트 구성 요소와 오프셋으로 구성됩니다. 이 그룹의 다른 모든 명령을 사용하면 한 쌍의 레지스터에서 메모리의 피연산자에 대한 전체 포인터를 얻을 수 있습니다. 이 경우 주소의 세그먼트 구성 요소가 배치되는 세그먼트 레지스터의 이름은 연산 코드에 의해 결정됩니다. 따라서 오프셋은 대상 피연산자가 나타내는 일반 레지스터에 배치됩니다.

그러나 소스 피연산자로 모든 것이 그렇게 단순하지는 않습니다. 실제로 소스로서의 명령에서는 포인터를 받고자 하는 메모리의 피연산자 이름을 직접 지정할 수 없습니다. 먼저 일부 메모리 영역에서 전체 포인터의 값을 가져와 get 명령에서 이 영역 이름의 전체 주소를 지정해야 합니다. 이 작업을 수행하려면 메모리 예약 및 초기화에 대한 지시문을 기억해야 합니다.

이러한 지시어를 적용할 때 피연산자 필드에 다른 데이터 정의 지시어의 이름(사실상 변수의 이름)이 지정되어 있는 특수한 경우가 가능합니다. 이 경우 이 변수의 주소는 메모리에 형성됩니다. 생성되는 주소(유효 또는 완전)는 적용된 지침에 따라 다릅니다. dw이면 유효 주소의 16비트 값만 메모리에 형성되고, dd이면 전체 주소가 메모리에 기록됩니다. 메모리에서 이 주소의 위치는 다음과 같습니다. 하위 워드에는 오프셋이 포함되고 상위 워드에는 주소의 16비트 세그먼트 구성 요소가 포함됩니다.

예를 들어 문자 체인으로 작업을 구성할 때 시작 주소를 특정 레지스터에 배치한 다음 체인 요소에 대한 순차적 액세스를 위해 루프에서 이 값을 수정하는 것이 편리합니다.

메모리에서 전체 데이터 포인터, 즉 세그먼트의 주소와 세그먼트 내의 오프셋 값을 얻기 위해 명령을 사용해야 하는 필요성은 특히 체인으로 작업할 때 발생합니다.

데이터 변환 명령

많은 마이크로프로세서 명령이 이 그룹에 속할 수 있지만 대부분은 다른 기능 그룹에 속해야 하는 특정 기능을 가지고 있습니다. 따라서 전체 마이크로프로세서 명령 집합 중에서 단 하나의 명령만 데이터 변환 명령에 직접적으로 기여할 수 있습니다. xlat [address_of_transcoding_table]

이것은 매우 흥미롭고 유용한 팀입니다. 그 효과는 al 레지스터의 값을 remap_table_address 피연산자가 지정한 주소에 있는 메모리 테이블의 다른 바이트로 바꾸는 것입니다.

"테이블"이라는 단어는 매우 조건적입니다. 실제로는 바이트 문자열일 뿐입니다. al 레지스터의 내용을 대체할 문자열의 바이트 주소는 합계 (bx) + (al)로 결정됩니다. 즉, al의 내용은 바이트 배열에서 인덱스 역할을 합니다.

xlat 명령으로 작업할 때 다음과 같은 미묘한 점에 주의하십시오. 명령이 새 값을 검색할 바이트 문자열의 주소를 지정하더라도 이 주소는 bx 레지스터에 미리 로드해야 합니다(예: lea 명령 사용). 따라서 lookup_table_address 피연산자는 실제로 필요하지 않습니다(피연산자의 선택성은 피연산자를 대괄호로 묶어서 표시됨). 바이트 문자열(트랜스코딩 테이블)은 크기가 1에서 255바이트(8비트 레지스터의 부호 없는 숫자 범위)의 메모리 영역입니다.

스택 명령

이 그룹은 스택을 사용하여 유연하고 효율적인 작업을 구성하는 데 중점을 둔 일련의 특수 명령입니다.

스택은 프로그램 데이터의 임시 저장을 위해 특별히 할당된 메모리 영역입니다. 스택의 중요성은 프로그램 구조에서 별도의 세그먼트가 제공된다는 사실에 의해 결정됩니다. 프로그래머가 자신의 프로그램에서 스택 세그먼트를 선언하는 것을 잊은 경우 tlink 링커는 경고 메시지를 발행합니다.

스택에는 세 개의 레지스터가 있습니다.

1) ss - 스택 세그먼트 레지스터

2) sp/esp - 스택 포인터 레지스터;

3) bp/ebp - 스택 프레임 베이스 포인터 레지스터.

스택 크기는 마이크로프로세서의 작동 모드에 따라 다르며 64KB(또는 보호 모드에서는 4GB)로 제한됩니다.

한 번에 하나의 스택만 사용할 수 있으며 세그먼트 주소는 SS 레지스터에 포함됩니다. 이 스택을 현재 스택이라고 합니다. 다른 스택을 참조하려면("스택 전환") 다른 주소를 ss 레지스터에 로드해야 합니다. SS 레지스터는 스택에서 작동하는 모든 명령을 실행하기 위해 프로세서에서 자동으로 사용됩니다.

스택 작업의 몇 가지 추가 기능을 나열합니다.

1) 스택에 대한 데이터 쓰기 및 읽기는 LIFO 원리에 따라 수행되며,

2) 데이터가 스택에 기록됨에 따라 후자는 더 낮은 주소로 증가합니다. 이 기능은 스택 작업을 위한 명령 알고리즘에 내장되어 있습니다.

3) 메모리 주소 지정을 위해 esp/sp 및 ebp/bp 레지스터를 사용할 때 어셈블러는 여기에 포함된 값이 ss 세그먼트 레지스터에 대한 오프셋이라고 자동으로 간주합니다.

일반적으로 스택은 그림 23과 같이 구성됩니다.

쌀. 23. 스택 조직의 개념도

SS, ESP/SP 및 EUR/BP 레지스터는 스택과 함께 작동하도록 설계되었습니다. 이러한 레지스터는 복잡한 방식으로 사용되며 각각 고유한 기능적 목적을 가지고 있습니다.

ESP/SP 레지스터는 항상 스택의 맨 위를 가리킵니다. 즉, 마지막 요소가 스택에 푸시된 오프셋을 포함합니다. 스택 명령은 이 레지스터를 암시적으로 변경하여 항상 스택에 푸시된 마지막 요소를 가리키도록 합니다. 스택이 비어 있으면 esp 값은 스택에 할당된 세그먼트의 마지막 바이트 주소와 같습니다. 요소가 스택으로 푸시되면 프로세서는 esp 레지스터의 값을 감소시킨 다음 요소를 새 정점의 주소에 씁니다. 스택에서 데이터를 꺼낼 때 프로세서는 최상위 주소에 있는 요소를 복사한 다음 스택 포인터 레지스터 esp의 값을 증가시킵니다. 따라서 주소가 감소하는 방향으로 스택이 점점 작아지는 것으로 나타났습니다.

맨 위가 아닌 스택 내부의 요소에 액세스해야 하는 경우에는 어떻게 해야 합니까? 이렇게 하려면 EBP 레지스터를 사용합니다. EBP 레지스터는 스택 프레임 기본 포인터 레지스터입니다.

예를 들어, 서브루틴을 입력할 때 일반적인 요령은 원하는 매개변수를 스택에 밀어넣어 전달하는 것입니다. 서브루틴이 스택과 함께 활발하게 작동하는 경우 이러한 매개변수에 대한 액세스가 문제가 됩니다. 탈출구는 필요한 데이터를 스택에 쓴 후 스택의 프레임(기본) 포인터(EUR 레지스터)에 스택의 맨 위 주소를 저장하는 것입니다. EUR 값은 나중에 전달된 매개변수에 액세스하는 데 사용할 수 있습니다.

스택의 시작 부분은 더 높은 메모리 주소에 있습니다. 그림 23에서 이 주소는 쌍 ss: fffF로 표시됩니다. 여기서 wT의 이동은 조건부로 주어진다. 실제로 이 값은 프로그래머가 프로그램에서 스택 세그먼트를 설명할 때 지정하는 값에 의해 결정됩니다.

스택 작업을 구성하기 위해 쓰기 및 읽기를 위한 특수 명령이 있습니다.

1. 소스 푸시 - 소스 값을 스택 맨 위에 씁니다.

흥미로운 것은 다음 작업을 포함하는 이 명령의 알고리즘입니다(그림 24).

1) (sp) = (sp) - 2; sp의 값은 2로 감소합니다.

2) 소스의 값이 ss: sp 쌍으로 지정된 주소에 기록됩니다.

쌀. 24. 푸시 명령 작동 방식

2. 팝 할당 - 스택 맨 위의 값을 대상 피연산자가 지정한 위치에 씁니다. 따라서 값은 스택의 맨 위에서 "제거"됩니다. pop 명령의 알고리즘은 push 명령의 알고리즘과 반대입니다(그림 25).

1) 대상 피연산자가 나타내는 위치에 스택 맨 위의 내용을 기록합니다.

2) (sp) = (sp) + 2; sp의 값을 증가시킵니다.

쌀. 25. pop 명령의 작동 방식

3. pusha - 스택에 대한 그룹 쓰기 명령입니다. 이 명령으로 ax, cx, dx, bx, sp, bp, si, di 레지스터가 스택에 순차적으로 기록됩니다. sp의 원래 내용, 즉 pusha 명령이 실행되기 전의 내용이 기록됩니다(그림 26).

쌀. 26. pusha 명령 작동 방식

4. pushaw는 pusha 명령과 거의 동의어입니다. 차이점이 무엇인가요? 비트 속성은 use16 또는 use32일 수 있습니다. pusha 및 pushaw 명령이 이러한 각 속성과 함께 작동하는 방식을 살펴보겠습니다.

1) use16 - pushaw 알고리즘은 pusha 알고리즘과 유사합니다.

2) use32 - pushaw는 변경되지 않습니다. pusha 명령은 설정된 세그먼트 너비에 민감하며 32비트 세그먼트가 지정되면 해당 32비트 레지스터, 즉 eax, esx, edx, ebx, esp, ebp, esi, edi와 함께 작동합니다.

5. pushad - pusha 명령과 유사하게 수행되지만 몇 가지 특성이 있습니다.

다음 세 가지 명령은 위 명령의 역순으로 수행됩니다.

1) 로라;

2) 포포;

3) 팝.

아래에 설명된 명령어 그룹을 사용하면 스택에 플래그 레지스터를 저장하고 스택에 워드 또는 더블 워드를 쓸 수 있습니다. 아래 나열된 명령어는 플래그 레지스터의 전체 내용에 대한 액세스를 허용(및 요구)하는 마이크로프로세서 명령어 세트의 유일한 명령어입니다.

1. pushf - 플래그의 레지스터를 스택에 저장합니다.

이 명령의 작동은 세그먼트 크기 속성에 따라 다릅니다.

1) 16 사용 - 크기가 2바이트인 플래그 레지스터가 스택에 기록됩니다.

2) use32 - 4바이트의 eflags 레지스터가 스택에 기록됩니다.

2. pushfw - 워드 크기의 플래그 레지스터를 스택에 저장합니다. 항상 use16 속성을 사용하여 pushf처럼 작동합니다.

3. pushfd - 세그먼트의 비트 너비 속성(즉, pushf와 동일)에 따라 스택에 플래그 또는 플래그 플래그 레지스터를 저장합니다.

마찬가지로 다음 세 가지 명령은 위에서 설명한 작업의 역순으로 수행합니다.

1) 팝프;

2) 팝프티비;

3) 팝.

결론적으로 스택 사용이 거의 불가피한 주요 작업 유형에 주목합니다.

1) 서브루틴 호출

2) 레지스터 값의 임시 저장;

3) 지역 변수의 정의.

2. 산술 명령어

마이크로프로세서는 정수 및 부동 소수점 연산을 수행할 수 있습니다. 이를 위해 아키텍처에는 두 개의 개별 블록이 있습니다.

1) 정수 연산을 수행하는 장치;

2) 부동 소수점 연산을 수행하는 장치.

이러한 각 장치에는 자체 명령 시스템이 있습니다. 원칙적으로 정수 장치는 부동 소수점 장치의 많은 기능을 대신할 수 있지만 계산 비용이 많이 듭니다. 어셈블리 언어를 사용하는 대부분의 문제는 정수 연산으로 충분합니다.

산술 명령어 및 데이터 그룹 개요

정수 컴퓨팅 장치는 27개가 조금 넘는 산술 명령을 지원합니다. 그림 XNUMX은 이 그룹의 명령 분류를 보여줍니다.

쌀. 27. 산술 명령의 분류

정수 산술 명령어 그룹은 두 가지 유형의 숫자와 함께 작동합니다.

1) 정수 이진수. 숫자에는 부호 있는 숫자가 있을 수도 있고 없을 수도 있습니다. 즉, 부호가 있거나 없는 숫자일 수 있습니다.

2) 정수 십진수.

이러한 데이터 유형이 저장되는 기계 형식을 고려하십시오.

정수 이진수

고정 소수점 이진 정수는 이진수 시스템으로 인코딩된 숫자입니다.

이진 정수의 차원은 8, 16 또는 32비트일 수 있습니다. 이진수의 부호는 숫자 표현의 최상위 비트가 해석되는 방식에 따라 결정됩니다. 해당 차원의 숫자에 대한 7,15 또는 31비트입니다. 동시에 산술 명령 중 이 최상위 비트를 부호 9로 고려하는 명령이 두 개뿐이라는 것이 흥미롭습니다. 이들은 정수 곱셈 및 나눗셈 명령 imul 및 idiv입니다. 다른 경우에는 부호 있는 숫자와 이에 따라 부호 비트가 있는 작업에 대한 책임이 프로그래머에게 있습니다. 이진수의 값 범위는 숫자의 최상위 비트 또는 숫자의 부호 비트로서의 최상위 비트의 크기 및 해석에 따라 다릅니다(표 XNUMX).

표 9. 이진수의 범위 십진수

28진수는 XNUMX비트 그룹으로 숫자의 각 XNUMX진수를 인코딩하는 원칙을 기반으로 하는 특수한 유형의 숫자 ​​정보 표현입니다. 이 경우 숫자의 각 바이트에는 소위 이진 코드 십진 코드(BCD - Binary-Coded Decimal)의 한두 자리가 포함됩니다. 마이크로프로세서는 BCD 번호를 두 가지 형식으로 저장합니다(그림 XNUMX).

1) 포장 형식. 이 형식에서 각 바이트는 두 개의 십진수를 포함합니다. 0진수는 9에서 4 사이의 4비트 이진수입니다. 이 경우, 숫자의 최상위 자리의 코드가 최상위 1비트를 차지합니다. 따라서 00바이트의 99진수 팩형 수의 표현 범위는 XNUMX에서 XNUMX까지입니다.

2) 포장되지 않은 형식. 이 형식에서 각 바이트는 4개의 최하위 비트에 하나의 십진수를 포함합니다. 상위 1비트는 0으로 설정됩니다. 이것은 소위 영역입니다. 따라서 9바이트에 압축을 푼 십진수를 표현할 수 있는 범위는 XNUMX부터 XNUMX까지입니다.

쌀. 28. BCD 번호의 표현

프로그램에서 이진 십진수를 설명하는 방법은 무엇입니까? 이렇게 하려면 두 개의 데이터 설명 및 초기화 지시문(db 및 dt)만 사용할 수 있습니다. BCD 번호를 설명하기 위해 이러한 지시어만 사용할 수 있는 가능성은 "낮은 주소의 낮은 바이트" 원칙이 이러한 번호에도 적용 가능하여 처리에 매우 편리하기 때문입니다. 그리고 일반적으로 BCD 번호와 같은 데이터 유형을 사용할 때 이러한 번호가 프로그램에서 설명되는 순서와 이를 처리하는 알고리즘은 프로그래머의 취향과 개인 취향의 문제입니다. 아래에서 BCD 번호 작업의 기본 사항을 살펴보면 명확해집니다.

이진 정수에 대한 산술 연산

무부호 이진수의 덧셈

마이크로 프로세서는 이진수 추가 규칙에 따라 피연산자 추가를 수행합니다. 결과 값이 피연산자 필드의 크기를 초과하지 않는 한 문제가 없습니다. 예를 들어 바이트 크기의 피연산자를 추가할 때 결과는 숫자 255를 초과하지 않아야 합니다. 이 경우 결과가 올바르지 않습니다. 왜 이런 일이 일어나는지 생각해 봅시다.

예를 들어 254진수로 5 + 259 = 11111110를 더해보자. 0000101 + 1 = 00000011 8. 결과는 9비트를 넘어 정확한 값은 8비트에 맞고 값 3은 피연산자의 0비트 필드에 남아 있는데 이는 물론 참이 아닙니다. 마이크로프로세서에서 이러한 추가 결과가 예측되고 이러한 상황을 수정하고 처리하기 위한 특별한 수단이 제공됩니다. 그래서 이 경우와 같이 결과의 비트 그리드를 벗어나는 상황을 수정하기 위해 캐리 플래그 cf를 의도합니다. EFLAGS/FLAGS 플래그 레지스터의 비트 XNUMX에 있습니다. 피연산자의 상위 순서에서 XNUMX이 전송된다는 사실을 고정하는 것은 이 플래그의 설정입니다. 당연히 프로그래머는 이러한 덧셈 연산 결과의 가능성을 고려하고 수정할 수 있는 수단을 제공해야 합니다. 여기에는 cf 플래그가 구문 분석되는 추가 작업 후 코드 섹션을 포함하는 작업이 포함됩니다. 이 플래그는 다양한 방법으로 구문 분석할 수 있습니다.

가장 쉽고 가장 접근하기 쉬운 방법은 jcc 조건부 분기 명령을 사용하는 것입니다. 이 명령은 피연산자로 현재 코드 세그먼트의 레이블 이름을 갖습니다. 이전 명령의 작동 결과 cf 플래그가 1로 설정된 경우 이 레이블로의 전환이 수행됩니다. 마이크로프로세서 명령 시스템에는 세 개의 이진 추가 명령이 있습니다.

1) inc 피연산자 - 증분 연산, 즉 피연산자의 값을 1씩 증가시킵니다.

2) 추가 operand_1, operand_2 - 작동 원리에 따른 추가 명령어: operand_1 = operand_1 + operand_2;

3) adc operand_1, operand_2 - 캐리 플래그를 고려한 추가 명령어 cf. 명령 작동 원리: operand_1 = operand_1 + operand_2 + value_sG.

마지막 명령에 주의하십시오. 이것은 상위 명령에서 하나를 전송하는 것을 고려하는 추가 명령입니다. 우리는 이미 그러한 단위의 출현 메커니즘을 고려했습니다. 따라서 adc 명령어는 마이크로프로세서에서 지원하는 표준 필드의 길이를 초과하는 크기의 긴 이진수를 추가하기 위한 마이크로프로세서 도구입니다.

부호 있는 이진 추가

사실, 마이크로프로세서는 부호 있는 숫자와 부호 없는 숫자의 차이를 "인식하지" 못합니다. 대신 그는 계산 과정에서 발생하는 특징적인 상황의 발생을 고정하는 수단을 가지고 있습니다. 무부호 덧셈에 대해 논의할 때 그 중 일부를 다루었습니다.

1) cf carry 플래그, 1로 설정하면 피연산자가 범위를 벗어났음을 나타냅니다.

2) 그러한 종료 ​​가능성을 고려하는 adc 명령(최하위 비트에서 수행).

또 다른 방법은 EFLAGS 레지스터(비트 11)의 오버플로 플래그를 사용하여 수행되는 피연산자의 상위(부호) 비트 상태를 등록하는 것입니다.

물론 컴퓨터에서 숫자가 어떻게 표현되는지 기억할 것입니다. 양수는 이진수, 음수는 XNUMX의 보수입니다. 숫자를 추가하기 위한 다양한 옵션을 고려하십시오. 예제는 피연산자의 가장 중요한 두 비트의 동작과 더하기 연산 결과의 정확성을 보여주기 위한 것입니다.

30566 = 0111011101100110

+

00687 = 00000010

=

31253 = 01111010

우리는 14번째와 15번째 숫자의 전송과 결과의 정확성을 모니터링합니다. 전송이 없고 결과가 정확합니다.

30566 = 0111011101100110

+

30566 = 0111011101100110

=

1132 = 11101110

14번째 카테고리에서 편입이 있었습니다. 15번째 범주에서 전송되지 않습니다. 오버플로가 있기 때문에 결과가 잘못되었습니다. 숫자 값이 16비트 부호 있는 숫자(+32 767)가 가질 수 있는 것보다 큰 것으로 나타났습니다.

-30566 = 10001000 10011010

+

-04875 = 11101100 11110101

=

-35441 = 01110101 10001111

15번째 자리에서 이체가 있었고 14번째 자리에서 이체가 없습니다. 음수 대신 양수로 판명되었기 때문에 결과가 올바르지 않습니다(최상위 비트는 0).

-4875 = 11101100 11110101

+

-4875 = 11101100 11110101

=

09750 = 11011001

14번째 및 15번째 비트에서 전송이 있습니다. 결과는 정확합니다.

따라서 우리는 모든 경우를 조사한 결과 전송 중에 오버플로우 상황(OF 플래그를 1로 설정)이 발생함을 발견했습니다.

1) 14번째 숫자부터(부호 있는 양수의 경우)

2) 15번째 자리부터(음수의 경우).

반대로 두 비트 모두 캐리가 있거나 두 비트 모두 캐리가 없는 경우 오버플로가 발생하지 않습니다(즉, OF 플래그가 0으로 재설정됨).

따라서 오버플로는 오버플로 플래그로 등록됩니다. 플래그 외에도 상위 비트에서 전송할 때 전송 플래그 CF는 1로 설정됩니다. 마이크로프로세서는 부호 있는 숫자와 부호 없는 숫자의 존재에 대해 알지 못하므로 프로그래머는 전적으로 결과 숫자. 조건부 점프 명령어 JC\JNC 및 JO\JNO를 각각 사용하여 CF 및 OF 플래그를 구문 분석할 수 있습니다.

부호가 있는 숫자를 더하는 명령은 부호가 없는 숫자와 동일합니다.

부호 없는 이진수 빼기

덧셈 연산의 분석에서와 마찬가지로 뺄셈 연산을 수행할 때 발생하는 프로세스의 본질에 대해 논의합니다. 피감수가 빼기보다 크면 문제가 없습니다. 차이는 양수이고 결과는 정확합니다. 피감수가 빼기보다 작으면 문제가 있는 것입니다. 결과는 0보다 작고 이것은 이미 부호 있는 숫자입니다. 이 경우 결과를 래핑해야 합니다. 이것은 무엇을 의미 하는가? 일반적인 빼기(열에서)를 사용하여 가장 높은 순서에서 1을 대출합니다. 마이크로프로세서는 동일한 작업을 수행합니다. 즉, 피연산자의 비트 그리드에서 가장 높은 숫자 다음의 숫자에서 1을 가져옵니다. 예를 들어 설명해 보겠습니다.

05 = 00000000

-10 = 00000000 00001010

뺄셈을 하려면, 해보자

선순위 가상 대출:

100000000 00000101

-

00000000 00001010

=

11111111 11111011

따라서 본질적으로 행동

(65 + 536) - 5 = 10

여기서 0은 숫자 65536에 해당합니다. 물론 결과는 정확하지 않지만 마이크로 프로세서는 캐리 플래그 cf를 설정하여 단위를 빌리는 사실을 수정하지만 모든 것이 정상이라고 생각합니다. 그러나 빼기 연산의 결과를 다시 주의 깊게 살펴보십시오. 5의 보수에서 -5입니다! 실험을 해봅시다. 차이를 10 + (-XNUMX)의 합으로 나타냅니다.

5 = 00000000

+

(-10)= 11111111 11110110

=

11111111 11111011

즉, 이전 예제와 동일한 결과를 얻었습니다.

따라서 무부호 빼기 명령 이후에는 CE 플래그의 상태를 분석해야 하는데, 1로 설정되어 있다면 이는 상위에서 차용이 있었고 그 결과를 추가 코드로 얻었다는 것을 의미한다. .

더하기 명령어와 마찬가지로 빼기 명령어 그룹은 가능한 가장 작은 집합으로 구성됩니다. 이러한 명령은 현재 고려 중인 알고리즘에 따라 빼기를 수행하며 예외는 프로그래머가 직접 고려해야 합니다. 빼기 명령에는 다음이 포함됩니다.

1) dec 피연산자 - 감소 연산, 즉 피연산자의 값을 1씩 감소시킵니다.

2) sub operand_1, operand_2 - 빼기 명령; 작동 원리: operand_1 = operand_1 - operand_2;

3) sbb operand_1, operand_2 - 대출을 고려한 빼기 명령(ci 플래그): operand_1 = operand_1 - operand_2 - value_sG.

보시다시피 빼기 명령 중 캐리 플래그 cf를 고려하는 sbb 명령이 있습니다. 이 명령은 adc와 유사하지만 이제 cf 플래그는 숫자를 뺄 때 최상위 숫자에서 1을 차용하는 표시기 역할을 합니다.

부호 있는 이진 빼기

여기 모든 것이 다소 복잡합니다. 마이크로프로세서에는 덧셈과 뺄셈이라는 두 가지 장치가 필요하지 않습니다. 추가 장치 하나만 ​​있으면 충분합니다. 그러나 추가 코드에서 부호가 있는 숫자를 더하는 방식으로 빼기의 경우 두 피연산자(감소 및 빼기)를 모두 나타내야 합니다. 결과는 또한 45의 보수 값으로 처리되어야 합니다. 그러나 여기서 어려움이 발생합니다. 우선 피연산자의 최상위 비트를 부호 비트로 간주한다는 사실과 관련이 있습니다. 127 - (-XNUMX)을 빼는 예를 고려하십시오.

부호있는 숫자의 빼기 1

45 = 0010

-

-127 = 1000 0001

=

-44 = 1010 1100

부호 비트로 판단하면 결과는 음수로 판명되었으며, 이는 숫자가 -44와 같은 보수로 간주되어야 함을 나타냅니다. 올바른 결과는 172여야 합니다. 여기서 부호 있는 덧셈의 경우와 마찬가지로 숫자의 유효 비트가 피연산자의 부호 비트를 변경했을 때 가수 오버플로가 발생했습니다. 오버플로 플래그의 내용으로 이 상황을 추적할 수 있습니다. 1로 설정하면 결과가 이 크기의 피연산자에 대한 부호 있는 숫자의 범위를 벗어남(즉, 최상위 비트가 변경됨)을 나타내며 프로그래머는 결과를 수정하기 위해 조치를 취해야 합니다.

부호있는 숫자의 빼기 2

-45-45 = -45 + (-45) = -90.

-45=11010011

+

-45=11010011

=

-90 = 1010 0110

여기서는 모든 것이 정상이며 오버플로 플래그는 0으로 재설정되고 부호 비트의 1은 결과 값이 XNUMX의 보수임을 나타냅니다.

큰 피연산자의 뺄셈과 덧셈

덧셈과 뺄셈 명령은 8, 16, 32비트와 같은 고정 차원의 피연산자와 함께 작동합니다. 그러나 48비트 피연산자를 사용하여 더 큰 차원의 숫자(예: 16비트)를 추가해야 하는 경우에는 어떻게 해야 합니까? 예를 들어 두 개의 48비트 숫자를 추가해 보겠습니다.

쌀. 29. 큰 피연산자 추가

그림 29는 긴 숫자를 추가하는 기술을 단계별로 보여줍니다. 멀티바이트 숫자를 추가하는 프로세스는 "열에" 두 개의 숫자를 추가할 때와 동일한 방식으로 발생함을 알 수 있습니다. 필요한 경우 구현을 통해 1을 최상위 비트로 전송합니다. 이 프로세스를 프로그래밍하면 더하기 및 빼기 작업을 수행할 수 있는 이진수의 범위가 크게 확장됩니다.

피연산자의 표준 비트 그리드를 초과하는 표현 범위를 가진 숫자를 빼는 원리는 더하기와 동일합니다. 즉, 캐리 플래그 cf가 사용됩니다. 열에서 빼는 과정을 상상하고 마이크로프로세서 명령어를 sbb 명령어와 올바르게 결합하면 됩니다.

더하기 및 빼기 명령어에 대한 논의를 마치려면 cf 및 of 플래그 외에도 eflags 레지스터에 이진 산술 명령어와 함께 사용할 수 있는 몇 가지 다른 플래그가 있습니다. 다음 플래그는 다음과 같습니다.

1) zf - 연산 결과가 1인 경우 0로 설정되고 결과가 1이 아닌 경우 0로 설정되는 제로 플래그.

2) sf - 산술 연산 후 값이 결과의 최상위 비트 값, 즉 비트 7, 15 또는 31과 일치하는 부호 플래그. 따라서 이 플래그는 연산에 사용할 수 있습니다. 서명된 숫자에.

부호 없는 숫자의 곱셈

부호 없는 숫자를 곱하는 명령은 다음과 같습니다.

멀티 팩터_1

보시다시피 명령에는 승수 피연산자가 하나만 포함되어 있습니다. 두 번째 피연산자 factor_2는 암시적으로 지정됩니다. 그 위치는 고정되어 있으며 요인의 크기에 따라 다릅니다. 일반적으로 곱셈의 결과는 인수보다 크기 때문에 곱셈의 크기와 위치도 고유하게 결정되어야 합니다. 요인의 크기에 대한 옵션과 두 번째 피연산자의 배치 및 결과는 표 10에 나와 있습니다.

표 10. 피연산자 배열 및 곱셈 결과

곱이 두 부분으로 구성되어 있고 피연산자의 크기에 따라 factor_2(하단) 및 추가 레지스터 ah, dx, edx(높은 부분). 그렇다면 동적으로(즉, 프로그램 실행 중에) 결과가 한 레지스터에 들어갈 만큼 충분히 작거나 레지스터의 크기를 초과하여 가장 높은 부분이 다른 레지스터에 있다는 것을 어떻게 알 수 있습니까? 이를 위해 이전 논의에서 이미 알려진 cf 및 overflow 플래그를 사용합니다.

1) 결과의 선행 부분이 0인 경우 제품 연산 후 플래그 cf = 0 및 of = XNUMX;

2) 이 플래그가 XNUMX이 아닌 경우 결과가 제품의 가장 작은 부분을 넘어섰고 두 부분으로 구성되었음을 의미하며 이는 추가 작업에서 고려해야 합니다.

부호 있는 숫자 곱하기

숫자에 부호를 곱하는 명령은 다음과 같습니다.

[이멀 피연산자_1, 피연산자_2, 피연산자_3]

이 명령은 mul 명령과 같은 방식으로 실행됩니다. imul 명령의 특징은 기호의 형성뿐입니다.

결과가 작고 한 레지스터에 맞는 경우(즉, cf = of = 0인 경우) 다른 레지스터(높은 부분)의 내용은 부호 확장입니다. 모든 비트는 높은 비트(부호 비트)와 같습니다. ) 결과의 낮은 부분. 그렇지 않으면(cf = of = 1인 경우) 결과의 부호는 결과의 상위 부분의 부호 비트이고 하위 부분의 부호 비트는 이진 결과 코드의 중요 비트입니다.

무부호 나누기

부호 없는 숫자를 나누는 명령은 다음과 같습니다.

div 구분선

제수는 메모리 또는 레지스터에 있을 수 있으며 크기는 8, 16 또는 32비트입니다. 피제수의 위치는 고정되어 있으며 곱셈 명령에서와 같이 피연산자의 크기에 따라 다릅니다. 나누기 명령의 결과는 몫과 나머지 값입니다.

나누기 연산의 피연산자 위치 및 크기에 대한 옵션은 표 11에 나와 있습니다.

표 11. 피연산자 배열 및 나눗셈 결과

나누기 명령이 실행된 후 플래그의 내용은 정의되지 않지만 "0으로 나누기"라는 인터럽트 번호 XNUMX이 발생할 수 있습니다. 이러한 유형의 중단은 소위 예외에 속합니다. 이러한 종류의 인터럽트는 컴퓨팅 프로세스 중 일부 이상으로 인해 마이크로프로세서 내부에서 발생합니다. 다음 이유 중 하나로 인해 div 명령을 실행하는 동안 인터럽트 O, "XNUMX으로 나누기"가 발생할 수 있습니다.

1) 제수가 XNUMX입니다.

2) 할당된 비트 그리드에 몫이 포함되지 않는 경우 다음과 같은 경우에 발생할 수 있습니다.

a) 워드 값의 피제수를 바이트 값의 제수로 나누고 그 피제수의 값이 제수의 값보다 256배 이상 큰 경우

b) 더블 워드 값의 피제수를 워드 값의 제수로 나누고 그 피제수의 값이 제수 값의 65배 이상인 경우

c) 4배 워드 값의 피제수를 더블 워드 값의 제수로 나누고 그 피제수의 값이 제수의 값보다 294배 이상 큰 경우.

기호로 구분

숫자를 부호로 나누는 명령은 다음과 같습니다.

idiv 분배기

이 명령의 경우 명령 및 부호 있는 숫자와 관련하여 고려된 모든 조항이 유효합니다. 부호가 있는 숫자의 경우 예외 0, "XNUMX으로 나누기" 발생의 특징만 기록합니다. 다음 이유 중 하나로 idiv 명령을 실행할 때 발생합니다.

1) 제수가 XNUMX입니다.

2) 할당된 비트 그리드에 몫이 포함되지 않습니다.

후자는 차례로 발생할 수 있습니다.

1) 부호 있는 워드 값을 가진 피제수를 부호 있는 바이트 값을 가진 제수로 나눌 때 피제수의 값이 제수 값의 128배 이상(따라서 몫이 -128의 범위를 벗어나지 않아야 함) ~ + 127);

2) 피제수를 부호 있는 더블워드 값으로 나눈 값을 부호 있는 워드 값으로 나눈 값이 제수 값의 32배 이상인 경우(따라서 몫이 -의 범위를 벗어나지 않아야 함) 768 ~ +32) ;

3) 피제수를 부호 있는 쿼드워드 값으로 부호 있는 더블 워드 제수로 나누고 피제수의 값이 제수 값의 2배 이상인 경우(따라서 몫이 -147에서 + 483 648 2 147).

정수 연산을 위한 보조 명령어

마이크로프로세서 명령어 세트에는 산술 계산을 수행하는 알고리즘을 보다 쉽게 ​​프로그래밍할 수 있는 몇 가지 명령어가 있습니다. 마이크로프로세서 개발자가 여러 명령을 제공한 해결을 위해 다양한 문제가 발생할 수 있습니다.

유형 변환 명령

산술 연산에 포함된 피연산자의 크기가 다른 경우에는 어떻게 됩니까? 예를 들어 더하기 연산에서 한 피연산자는 워드이고 다른 피연산자는 더블 워드라고 가정합니다. 위에서 동일한 형식의 피연산자가 더하기 연산에 참여해야 한다고 말했습니다. 숫자에 부호가 없으면 출력을 쉽게 찾을 수 있습니다. 이 경우 원래 피연산자를 기반으로 새로운 피연산자(더블 워드 형식)를 형성할 수 있으며 상위 비트는 단순히 64으로 채울 수 있습니다. 부호 있는 숫자의 경우 상황이 더 복잡합니다. 프로그램 실행 중에 피연산자의 부호를 동적으로 고려하는 방법은 무엇입니까? 이러한 문제를 해결하기 위해 마이크로프로세서 명령어 세트에는 소위 유형 변환 명령어가 있습니다. 이러한 명령어는 바이트를 워드로, 워드를 더블 워드로, 더블 워드를 쿼드 워드(XNUMX비트 값)로 확장합니다. 유형 변환 명령은 부호 있는 정수를 변환할 때 특히 유용합니다. 새로 구성된 피연산자의 상위 비트를 이전 객체의 부호 비트 값으로 자동으로 채우기 때문입니다. 이 연산은 원본과 동일한 부호와 크기의 정수 값을 생성하지만 더 긴 형식입니다. 이러한 변환을 부호 전파 연산이라고 합니다.

유형 변환 명령에는 두 가지 종류가 있습니다.

1. 피연산자가 없는 명령어. 다음 명령은 고정 레지스터에서 작동합니다.

1) cbw(바이트를 워드로 변환) - 상위 비트 al의 값을 ah 레지스터의 모든 비트에 확산시켜 바이트(al 레지스터 내)를 워드(ah 레지스터 내)로 변환하는 명령.

2) cwd(워드를 더블로 변환) - 상위 비트 ax의 값을 레지스터 dx의 모든 비트로 확산하여 워드(레지스터 ax에 있음)를 더블 워드(레지스터 dx: ax에 있음)로 변환하는 명령.

3) cwde(워드를 더블로 변환) - 상위 비트 ax의 값을 eax 레지스터의 상위 절반의 모든 비트에 확산하여 워드(레지스터 ax에 있음)를 더블 워드(레지스터 eax에 있음)로 변환하는 명령 ;

4) cdq (Convert Double Word to Quarter Word) - eax의 최상위 비트 값을 모두 edx 레지스터의 비트.

2. 문자열 처리 명령과 관련된 명령 movsx 및 movzx. 이 명령은 문제의 맥락에서 유용한 속성을 가지고 있습니다.

1) movsx operand_1, operand_2 - 부호 전파와 함께 보냅니다. 레지스터 또는 메모리 피연산자일 수 있는 operand_8의 16비트 또는 2비트 값을 레지스터 중 하나의 16비트 또는 32비트 값으로 확장하고 부호 비트 값을 사용하여 operand_1의 상위 위치를 채웁니다. 이 명령어는 산술 연산을 위해 부호 있는 피연산자를 준비하는 데 유용합니다.

2) movzx operand_1, operand_2 - 확장자가 8인 상태로 보냅니다. operand_16의 2비트 또는 16비트 값을 32비트 또는 2비트로 확장하여 operand_XNUMX의 높은 위치를 XNUMX으로 지웁니다(채웁니다). 이 명령은 산술을 위해 부호 없는 피연산자를 준비하는 데 유용합니다.

기타 유용한 명령

1. xadd 대상, 소스 - 교환 및 추가.

이 명령을 사용하면 두 가지 작업을 순서대로 수행할 수 있습니다.

1) 대상 및 소스 값을 교환합니다.

2) 합계 대신 목적지 피연산자를 배치합니다: 목적지 = 목적지 + 소스.

2. 부정 피연산자 - XNUMX의 보수를 사용한 부정.

명령어는 피연산자의 값을 반전시킵니다. 물리적으로 이 명령은 한 가지 작업을 수행합니다.

피연산자 = 0 - 피연산자, 즉 XNUMX에서 피연산자를 뺍니다.

neg 피연산자 명령을 사용할 수 있습니다.

1) 기호를 변경하려면

2) 상수에서 빼기를 수행합니다.

이진 십진수에 대한 산술 연산

이 섹션에서는 압축 및 압축 해제된 BCD 숫자에 대한 네 가지 기본 산술 연산의 세부 사항을 살펴보겠습니다.

질문이 올바르게 발생할 수 있습니다. BCD 번호가 필요한 이유는 무엇입니까? 대답은 다음과 같을 수 있습니다. BCD 번호는 비즈니스 응용 프로그램, 즉 숫자가 크고 정확해야 하는 경우에 필요합니다. 이진수의 예에서 이미 보았듯이 이러한 숫자를 사용한 연산은 어셈블리 언어에서 상당히 문제가 됩니다. 이진수 사용의 단점은 다음과 같습니다.

1) 워드 및 더블 워드 형식의 값은 범위가 제한되어 있습니다. 프로그램이 금융 분야에서 작동하도록 설계된 경우 루블 금액을 65(단어) 또는 536(두 단어)으로 제한하면 적용 범위가 크게 좁아집니다.

2) 반올림 오류가 있습니다. 이진 정수로 작동할 때 잔액의 가치를 고려하지 않고 수십억 달러로 작동하는 프로그램이 은행 어딘가에서 실행되는 것을 상상할 수 있습니까? 나는 그런 프로그램의 저자가 되고 싶지 않습니다. 부동 소수점 숫자를 사용하면 저장되지 않습니다. 동일한 반올림 문제가 존재합니다.

3) 기호 형식(ASCII 코드)으로 많은 양의 결과 표시. 비즈니스 프로그램은 계산만 하는 것이 아닙니다. 사용 목적 중 하나는 사용자에게 정보를 신속하게 전달하는 것입니다. 이를 위해서는 물론 정보가 상징적 형태로 제시되어야 합니다. 숫자를 이진수에서 ASCII로 변환하려면 약간의 계산 작업이 필요합니다. 부동 소수점 숫자는 기호 형식으로 변환하기가 훨씬 더 어렵습니다. 그러나 압축 해제된 30진수 숫자의 XNUMX진수 표현과 ASCII 테이블에서 해당 문자를 보면 XNUMXh 차이가 난다는 것을 알 수 있습니다. 따라서 기호 형식으로 또는 그 반대로 변환하는 것이 훨씬 쉽고 빠릅니다.

적어도 십진수로 행동의 기본을 마스터하는 것의 중요성을 이미 보았을 것입니다. 다음으로 십진수로 기본 산술 연산을 수행하는 기능을 고려하십시오. BCD 숫자의 덧셈, 뺄셈, 곱셈 및 나눗셈에 대한 별도의 명령이 없다는 사실을 즉시 알 수 있습니다. 이것은 매우 이해할 수 있는 이유로 수행되었습니다. 이러한 숫자의 차원은 임의로 클 수 있습니다. BCD 숫자는 압축된 숫자와 압축 해제된 숫자 모두 더하고 뺄 수 있지만 압축 해제된 BCD 숫자만 나누고 곱할 수 있습니다. 이것이 왜 그렇게 되었는지는 더 많은 논의를 통해 알게 될 것입니다.

압축 해제된 BCD 숫자에 대한 산술

압축 해제된 BCD 번호 추가

덧셈의 ​​두 가지 경우를 생각해 봅시다.

덧셈 결과가 9 이하

6 = 0000

+

3 = 0000

=

9 = 0000

주니어에서 시니어 테트라드로의 이전은 없습니다. 결과는 정확합니다.

덧셈 결과가 9보다 큽니다.

06 = 0000

+

07 = 0000

=

13 = 0000

더 이상 BCD 번호를 받지 못했습니다. 결과가 잘못되었습니다. 압축을 푼 BCD 형식의 올바른 결과는 이진수로 0000 0001 0000 0011(또는 십진수로 13)이어야 합니다.

BCD 번호를 추가할 때 이 문제(및 다른 산술 연산을 수행할 때 유사한 문제)와 가능한 해결 방법을 분석한 후 마이크로프로세서 명령 시스템 개발자는 BCD 번호 작업을 위한 특수 명령을 도입하지 않고 몇 가지 수정 명령을 도입하기로 결정했습니다. .

이 명령어의 목적은 피연산자가 BCD 숫자인 경우 일반 산술 명령어의 연산 결과를 수정하는 것입니다.

예제 10의 뺄셈의 경우 얻은 결과를 수정해야 함을 알 수 있습니다. 마이크로프로세서 명령 시스템에서 압축 해제된 두 개의 한 자리 BCD 번호를 추가하는 작업을 수정하기 위해 특수 명령인 aaa(ASCII Adjust for Addition) - 기호 형식으로 표현하기 위한 추가 결과 수정이 있습니다.

이 명령에는 피연산자가 없습니다. al 레지스터에서만 암시적으로 작동하며 하위 테트라드의 값을 구문 분석합니다.

1) 이 값이 9보다 작으면 플래그 cf가 XNUMX으로 재설정되고 다음 명령어로의 전환이 수행됩니다.

2) 이 값이 9보다 크면 다음 작업이 수행됩니다.

a) 6이 하위 tetrad al의 내용에 추가됩니다(그러나 전체 레지스터의 내용에는 포함되지 않습니다!) 따라서 십진법 결과의 값이 올바른 방향으로 수정됩니다.

b) 플래그 cf는 1로 설정되어 후속 작업에서 고려할 수 있도록 최상위 비트로 전송을 고정합니다.

따라서 예제 10에서 합계 값 0000 1101이 al에 있다고 가정하면 aaa 명령 후 레지스터는 1101 + 0110 = 0011, 즉 이진수 0000 0011 또는 십진수 3을 가지며 cf 플래그는 1로 설정됩니다. 즉, 전송이 마이크로프로세서에 저장되었습니다. 다음으로 프로그래머는 이전 비트의 캐리를 고려하는 adc 추가 명령을 사용해야 합니다.

압축 해제된 BCD 번호 빼기

여기서의 상황은 덧셈과 매우 유사합니다. 같은 경우를 생각해 봅시다.

빼기 결과는 9보다 크지 않습니다.

6 = 0000

-

3 = 0000

=

3 = 0000

보시다시피 시니어 노트북에서 대출이 없습니다. 결과는 정확하며 수정할 필요가 없습니다.

빼기 결과가 9보다 큽니다.

6 = 0000

-

7 = 0000

=

-1 = 1111 1111

빼기는 이진 산술 규칙에 따라 수행됩니다. 따라서 결과는 BCD 번호가 아닙니다.

압축을 푼 BCD 형식의 올바른 결과는 9(이진법으로 0000 1001)여야 합니다. 이 경우 정상적인 빼기 명령과 마찬가지로 최상위 자리에서 빌린 것으로 가정합니다. 즉, BCD 숫자의 경우 실제로 16 - 7의 빼기가 수행되어야 합니다. 더하기의 경우 빼기 결과를 수정해야 합니다. 이를 위해 기호 형식으로 표현하기 위해 빼기 결과를 수정하는 특수 명령인 aas(빼기를 위한 ASCII 조정)가 있습니다.

또한 aas 명령어에는 피연산자가 없으며 al 레지스터에서 작동하여 다음과 같이 최소 차수 테트라드를 구문 분석합니다.

1) 값이 9보다 작으면 cf 플래그가 0으로 재설정되고 제어가 다음 명령으로 전달됩니다.

2) al의 tetrad 값이 9보다 큰 경우 aas 명령은 다음 작업을 수행합니다.

a) 레지스터 al의 하위 테트라드의 내용에서 6을 뺍니다(주 - 전체 레지스터의 내용이 아님).

b) 레지스터 al의 상위 테트라드를 재설정합니다.

c) cf 플래그를 1로 설정하여 가상 상위 차용을 고정합니다.

aas 명령이 기본 sub 및 sbb 빼기 명령과 함께 사용된다는 것은 분명합니다. 이 경우 하위 명령을 한 번만 사용하는 것이 합리적이며 피연산자의 가장 낮은 자릿수를 뺄 때 sbb 명령을 사용해야 가장 높은 순서에서 가능한 대출을 고려합니다.

압축 해제된 BCD 숫자의 곱셈

압축 해제된 숫자를 더하고 빼는 예를 사용하여 BCD 숫자에 대해 이러한 작업을 수행하기 위한 표준 알고리즘이 없으며 프로그래머가 자신의 프로그램 요구 사항에 따라 이러한 작업을 구현해야 한다는 것이 분명해졌습니다.

나머지 두 연산인 곱셈과 나눗셈의 구현은 훨씬 더 복잡합니다. 마이크로프로세서 명령어 세트에는 압축 해제된 한 자리 BCD 숫자의 곱셈 및 나눗셈을 생성하는 수단만 있습니다.

임의의 차원의 수를 곱하기 위해서는 곱셈 알고리즘(예: "in a column")을 기반으로 곱셈 프로세스를 직접 구현해야 합니다.

두 개의 한 자리 BCD 숫자를 곱하려면 다음을 수행해야 합니다.

1) 요소 중 하나를 AL 레지스터에 배치합니다(mul 명령에서 요구하는 대로).

2) 두 번째 피연산자를 레지스터 또는 메모리에 배치하여 바이트를 할당합니다.

3) 인수를 mul 명령으로 곱합니다(예상대로 결과는 ah에 있음).

4) 결과는 당연히 바이너리 코드로 나오므로 수정이 필요합니다.

곱셈 후 결과를 수정하기 위해 aam (ASCII Adjust for Multiplication) - 기호 형식으로 표현하기 위해 곱셈 결과 수정과 같은 특수 명령이 사용됩니다.

피연산자가 없으며 다음과 같이 AX 레지스터에서 작동합니다.

1) al을 10으로 나눕니다.

2) 나눗셈 결과는 몫은 al, 나머지는 ah로 쓴다. 결과적으로 aam 명령을 실행한 후 AL 및 ah 레지스터에는 두 자리 곱의 올바른 BCD 자리가 포함됩니다.

aam 명령에 대한 논의를 끝내기 전에 해당 명령의 용도를 한 가지 더 언급해야 합니다. 이 명령은 AL 레지스터의 이진수를 압축이 풀린 BCD 숫자로 변환하는 데 사용할 수 있으며, 이 숫자는 ah 레지스터에 배치됩니다. 결과의 가장 중요한 숫자는 ah에 있고, 최하위 숫자는 al에 있습니다. 이진수는 0~99 범위에 있어야 합니다.

압축 해제된 BCD 번호의 나눗셈

두 개의 압축 해제된 BCD 번호의 나눗셈 연산을 수행하는 프로세스는 이전에 고려했던 다른 연산과 다소 다릅니다. 여기에서도 수정 조치가 필요하지만 하나의 BCD 번호를 다른 BCD 번호로 직접 나누는 주요 작업 전에 수행해야 합니다. 먼저 레지스터 아에서 포장되지 않은 두 개의 배당금 BCD 숫자를 가져와야 합니다. 이것은 프로그래머를 어느 정도 편안하게 만듭니다. 다음으로 aad - aad(ASCII Adjust for Division) - 기호 표현을 위한 분할 수정 명령을 실행해야 합니다.

이 명령어에는 피연산자가 없으며 ax 레지스터에 있는 압축 해제된 두 자리 BCD 숫자를 이진수로 변환합니다. 이 이진수는 이후 나눗셈 연산에서 배당금 역할을 하게 됩니다. 변환 외에도 aad 명령은 결과 이진수를 AL 레지스터에 배치합니다. 배당금은 당연히 0~99 범위의 이진수입니다.

aad 명령이 이 변환을 수행하는 알고리즘은 다음과 같습니다.

1) ah(AH의 내용)에 있는 원래 BCD 번호의 가장 높은 숫자에 10을 곱합니다.

2) 추가 AH + AL을 수행하고 그 결과 (이진수)가 AL에 입력됩니다.

3) AN의 내용을 재설정합니다.

다음으로 프로그래머는 ax의 내용을 바이트 레지스터 또는 바이트 메모리 위치에 있는 단일 BCD 숫자로 나누기 위해 일반 div 나누기 명령을 실행해야 합니다.

aash와 마찬가지로 aad 명령을 사용하여 0~99 범위의 압축을 푼 BCD 숫자를 해당 이진수로 변환할 수도 있습니다.

더 큰 용량의 숫자를 나누려면 곱셈의 경우와 같이 "열에서"와 같은 자체 알고리즘을 구현하거나 더 최적의 방법을 찾아야 합니다.

압축된 BCD 숫자의 산술

위에서 언급했듯이 압축된 BCD 숫자는 더하기와 빼기만 가능합니다. 다른 작업을 수행하려면 압축을 푼 형식이나 이진 표현으로 추가 변환해야 합니다. 압축된 BCD 번호는 큰 관심이 없기 때문에 간략하게 살펴보겠습니다.

압축된 BCD 번호 추가

먼저 문제의 핵심으로 이동하여 두 자리 압축된 BCD 숫자 두 개를 추가해 보겠습니다. 압축된 BCD 번호를 추가하는 예:

67 = 01100111

+

75 = 01110101

=

142 = 1101 1100 = 220

보시다시피 이진법 결과는 1101 1100(또는 십진수로 220)이며 이는 잘못된 것입니다. 이는 마이크로프로세서가 BCD 번호의 존재를 인지하지 못하고 이진수 추가 규칙에 따라 추가하기 때문입니다. 실제로 BCD의 결과는 0001 0100 0010(또는 십진수로 142)이어야 합니다.

포장되지 않은 BCD 번호의 경우 포장된 BCD 번호의 경우 산술 연산의 결과를 어떻게든 수정해야 할 필요가 있음을 알 수 있습니다.

마이크로프로세서는 이 명령 daa - daa(덧셈을 위한 십진수 조정) - 십진수 형식으로 표시하기 위해 더하기 결과 수정을 제공합니다.

daa 명령은 daa 명령의 설명에 제공된 알고리즘에 따라 al 레지스터의 내용을 두 개의 압축 십진수로 변환합니다.결과 단위(더하기 결과가 99보다 큰 경우)는 cf 플래그에 저장됩니다. 따라서 최상위 비트로의 전송을 고려합니다.

압축된 BCD 숫자 빼기

덧셈과 마찬가지로 마이크로프로세서는 압축된 BCD 번호를 이진수로 취급하고 이에 따라 BCD 번호를 이진수로 뺍니다.

압축된 BCD 숫자 빼기.

67-75를 뺍니다. 마이크로프로세서는 덧셈 방식으로 뺄셈을 수행하므로 다음을 따릅니다.

67 = 01100111

+

-75=10110101

=

-8 = 0001 1100 = 28

보시다시피 결과는 28진수로 0000이며 터무니 없습니다. BCD에서 결과는 1000 8(또는 XNUMX진수로 XNUMX)이어야 합니다.

압축된 BCD 숫자의 빼기를 프로그래밍할 때 프로그래머는 압축되지 않은 BCD 숫자를 빼는 경우와 마찬가지로 부호를 직접 제어해야 합니다. 이는 상위 차용을 수정하는 CF 플래그를 사용하여 수행됩니다.

BCD 숫자 자체의 빼기는 간단한 sub 또는 sbb 빼기 명령으로 수행됩니다. 결과 수정은 명령 das - das(빼기의 소수점 조정) - 소수점 형식으로 표현하기 위한 빼기 결과의 수정으로 수행됩니다.

das 명령은 das 명령의 설명에 제공된 알고리즘에 따라 AL 레지스터의 내용을 두 개의 압축된 XNUMX진수로 변환합니다.

강의 19. 제어 전송 명령

1. 논리 명령

산술 계산 수단과 함께 마이크로프로세서 명령 시스템에는 논리적 데이터 변환 수단도 있습니다. 형식 논리의 규칙을 기반으로 하는 이러한 데이터 변환은 논리적 수단으로 이루어집니다.

형식 논리는 참과 거짓 진술 수준에서 작동합니다. 마이크로프로세서의 경우 이는 일반적으로 각각 1과 0을 의미합니다. 컴퓨터의 경우 XNUMX과 XNUMX의 언어는 고유하지만 기계 명령이 작동하는 데이터의 최소 단위는 바이트입니다. 그러나 시스템 수준에서는 가능한 가장 낮은 수준인 비트 수준에서 작동할 수 있어야 하는 경우가 많습니다.

쌀. 29. 논리적 데이터 처리 수단

논리적 데이터 변환 수단에는 논리적 명령과 논리적 연산이 포함됩니다. 어셈블러 명령어의 피연산자는 일반적으로 연산자와 피연산자의 조합인 표현식이 될 수 있습니다. 이러한 연산자 중에는 식 개체에 대한 논리 연산을 구현하는 연산자가 있을 수 있습니다.

이러한 도구를 자세히 고려하기 전에 논리적 데이터 자체가 무엇이며 어떤 작업이 수행되는지 살펴보겠습니다.

부울 데이터

논리적 데이터 처리의 이론적 기반은 형식 논리입니다. 여러 가지 논리 체계가 있습니다. 가장 유명한 것 중 하나는 명제 미적분입니다. 명제는 참 또는 거짓이라고 말할 수 있는 모든 진술입니다.

명제 미적분은 명제 조합의 참 또는 거짓을 결정하는 데 사용되는 일련의 규칙입니다.

명제 미적분은 컴퓨터의 원리 및 프로그래밍의 기본 방법과 매우 조화롭게 결합됩니다. 컴퓨터의 모든 하드웨어 구성 요소는 논리 칩에 내장되어 있습니다. 가장 낮은 수준의 컴퓨터에서 정보를 표현하는 시스템은 비트 개념을 기반으로 합니다. 두 가지 상태(0(거짓)와 1(참))만 있는 비트는 자연스럽게 명제 계산에 적합합니다.

이론에 따르면 다음과 같은 논리 연산이 명령문(비트에서)에 대해 수행될 수 있습니다.

1. 부정(논리적 NOT) - 한 피연산자에 대한 논리적 연산으로, 그 결과는 원래 피연산자 값의 역수입니다.

이 작업은 다음 진리표(표 12)에 의해 고유하게 특징지어집니다.

표 12. 논리 부정에 대한 진리표

2. 논리적 덧셈(논리적 포함 OR) - 두 피연산자에 대한 논리적 연산으로, 하나 또는 두 피연산자가 모두 참(1)이면 결과가 "참"(1)이고 두 피연산자가 모두 참이면 "거짓"(0)입니다. 거짓(0).

이 작업은 다음 진리표를 사용하여 설명됩니다(표 13).

표 13. 논리 포함 OR의 진리표

3. 논리적 곱셈(논리 AND) - 두 피연산자에 대한 논리적 연산으로, 두 피연산자가 모두 참(1)인 경우에만 결과가 참(1)입니다. 다른 모든 경우에 연산 값은 "거짓"(0)입니다.

이 작업은 다음 진리표를 사용하여 설명됩니다(표 14).

표 14. 논리 AND 진리표

4. 논리적 배타적 덧셈(Logical Exclusive OR) - 두 피연산자에 대한 논리 연산으로, 두 피연산자 중 하나만 참(1)이면 결과가 "참"(1)이고 거짓(0)이면 거짓(0)입니다. 두 피연산자는 false(1) 또는 true(15)입니다. 이 작업은 다음 진리표를 사용하여 설명됩니다(표 XNUMX).

표 15. 논리적 XOR에 대한 진리표

마이크로프로세서 명령어 세트에는 이러한 작업을 지원하는 16개의 명령어가 포함되어 있습니다. 이러한 명령어는 피연산자의 비트에 대해 논리 연산을 수행합니다. 물론 피연산자의 차원은 동일해야 합니다. 예를 들어, 피연산자의 차원이 워드(0비트)와 같으면 피연산자의 XNUMX비트에서 논리 연산이 먼저 수행되고 그 결과가 결과의 비트 XNUMX 자리에 기록됩니다. 다음으로 명령은 첫 번째부터 XNUMX번째까지 모든 비트에서 이러한 작업을 순차적으로 반복합니다.

논리 명령

마이크로프로세서 명령 시스템에는 논리 데이터 작업을 지원하는 다음과 같은 명령 세트가 있습니다.

1) 및 operand_1, operand_2 - 논리 곱셈 연산. 명령은 피연산자 operand_1 및 operand_2의 비트에 대해 비트 논리 AND 연산(연결)을 수행합니다. 결과는 operand_1 대신 기록됩니다.

2) og operand_1, operand_2 - 논리 추가 연산. 이 명령은 피연산자 operand_1 및 operand_2의 비트에서 비트 논리 OR 연산(분리)을 수행합니다. 결과는 operand_1 대신 기록됩니다.

3) xor operand_1, operand_2 - 논리적 배타적 추가 연산. 이 명령은 피연산자 operand_1 및 operand_2의 비트에 대해 비트 논리 XOR 연산을 수행합니다. 결과는 피연산자 대신 기록됩니다.

4) 테스트 operand_1, operand_2 - "테스트" 연산(논리 곱셈 방법 사용). 이 명령은 피연산자 operand_1 및 operand_2의 비트에 대해 비트 논리 AND 연산을 수행합니다. 피연산자의 상태는 동일하게 유지되고 플래그 zf, sf 및 pf만 변경되므로 상태를 변경하지 않고 피연산자의 개별 비트 상태를 분석할 수 있습니다.

5) 피연산자 아님 - 논리 부정 연산. 이 명령은 피연산자의 각 비트에 대해 비트 반전(값을 반대 값으로 대체)을 수행합니다. 피연산자 대신 결과가 기록됩니다.

마이크로프로세서 명령 세트에서 논리적 명령의 역할을 이해하려면 해당 응용 분야와 프로그래밍에서 사용하는 일반적인 방법을 이해하는 것이 매우 중요합니다.

논리 명령의 도움으로 설정, 재설정, 반전 또는 단순히 특정 값을 확인하기 위해 피연산자에서 개별 비트를 선택할 수 있습니다.

이러한 작업을 비트로 구성하기 위해 operand_2는 일반적으로 마스크 역할을 합니다. 비트 1에 설정된 이 마스크의 비트 덕분에 특정 작업에 필요한 operand_1 비트가 결정됩니다. 이 목적을 위해 어떤 논리 명령을 사용할 수 있는지 보여드리겠습니다.

1) 특정 숫자(비트)를 1로 설정하려면 og operand_1, operand_2 명령을 사용합니다.

이 명령에서 마스크 역할을 하는 operand_2는 operand_1에서 1로 설정되어야 하는 비트 대신 XNUMX비트를 포함해야 합니다.

2) 특정 숫자(비트)를 0으로 재설정하려면 명령과 operand_1, operand_2를 사용합니다.

이 명령어에서 마스크 역할을 하는 operand_2는 operand_0에서 1으로 설정되어야 하는 비트 대신 XNUMX 비트를 포함해야 합니다.

3) 명령 xor operand_1, operand_2가 적용됩니다.

a) operand_1과 피연산자의 어떤 비트가 다른지 알아내기 위해;

b) operand_1에서 지정된 비트의 상태를 반전합니다.

xor 명령을 실행할 때 관심 있는 마스크 비트(operand_2)는 단일이어야 하고 나머지는 XNUMX이어야 합니다.

명령 test operand_1, operand_2(check operand_1)는 지정된 비트의 상태를 확인하는 데 사용됩니다.

마스크(operand_1)에 있는 operand_2의 검사된 비트는 1로 설정되어야 합니다. 테스트 명령의 알고리즘은 and 명령의 알고리즘과 유사하지만 operand_XNUMX의 값을 변경하지 않습니다. 명령의 결과는 제로 플래그 zf의 값을 설정하는 것입니다.

1) zf = 0이면 논리 곱의 결과로 XNUMX 결과, 즉 피연산자의 해당 단위 비트와 일치하지 않는 마스크의 한 단위 비트를 얻습니다.

2) zf = 1이면 논리 곱의 결과로 1이 아닌 결과가 얻어집니다. 즉, 마스크의 적어도 하나의 단위 비트가 operand_XNUMX의 해당 단위 비트와 일치합니다.

테스트 명령의 결과에 반응하려면 점프 명령 jnz 레이블(0이 아닌 경우 점프) - 제로 플래그 zf가 XNUMX이 아닌 경우 점프 또는 역 동작 명령 - jz 레이블(XNUMX이 아닌 경우 점프)을 사용하는 것이 좋습니다. ) - 제로 플래그 zf = XNUMX이면 점프합니다.

다음 두 명령은 1로 설정된 첫 번째 피연산자 비트를 검색합니다. 피연산자의 시작과 끝 모두에서 검색을 수행할 수 있습니다.

1) bsf operand_1, operand_2(Bit Scanning Forward) - 비트를 앞으로 스캔합니다. 이 명령어는 2로 설정된 첫 번째 비트를 찾기 위해 operand_0의 비트를 최하위에서 최상위로(비트 1에서 최상위 비트로) 검색(스캔)합니다. 이 비트를 정수 값으로. operand_1의 모든 비트가 2이면 제로 플래그 zf가 0로 설정되고, 그렇지 않으면 zf 플래그가 1으로 재설정됩니다.

2) bsr operand_1, operand_2(Bit Scanning Reset) - 비트를 역순으로 스캔합니다. 이 명령어는 2로 설정된 첫 번째 비트를 찾기 위해 operand_0의 비트를 최상위에서 최하위로(최상위 비트에서 비트 1으로) 검색(스캔)합니다. 이 비트를 정수 값으로. 왼쪽에 있는 첫 번째 단위 비트의 위치는 여전히 비트 1을 기준으로 계산되는 것이 중요합니다. operand_0의 모든 비트가 2이면 제로 플래그 zf가 0로 설정되고 그렇지 않으면 zf 플래그가 1으로 재설정됩니다.

Intel 마이크로프로세서의 최신 모델에서는 피연산자의 특정 비트에 액세스할 수 있도록 하는 논리적 명령 그룹에 몇 가지 추가 명령이 나타납니다. 피연산자는 메모리 또는 일반 레지스터에 있을 수 있습니다. 비트 위치는 피연산자의 최하위 비트에 상대적인 비트 오프셋으로 지정됩니다. 오프셋 값은 직접 값으로 지정하거나 범용 레지스터에 포함할 수 있습니다. bsr 및 bsf 명령의 결과를 오프셋 값으로 사용할 수 있습니다. 모든 명령어는 선택한 비트의 값을 CE 플래그에 할당합니다.

1) bt 피연산자, bit_offset(비트 테스트) - 비트 테스트. 명령은 비트 값을 cf 플래그로 전송합니다.

2) bts 피연산자, offset_bit(비트 테스트 및 설정) - 비트 확인 및 설정. 명령어는 비트 값을 CF 플래그로 전송한 다음 검사할 비트를 1로 설정합니다.

3) btr 피연산자, bit_offset(비트 테스트 및 재설정) - 비트 확인 및 재설정. 명령은 비트 값을 CF 플래그로 전송한 다음 이 비트를 0으로 설정합니다.

4) btc 피연산자, offset_bit(비트 테스트 및 변환) - 비트 확인 및 반전. 명령은 cf 플래그의 비트 값을 래핑한 다음 해당 비트의 값을 반전시킵니다.

시프트 명령

이 그룹의 명령어는 피연산자의 개별 비트 조작도 제공하지만 위에서 설명한 논리 명령어와는 다른 방식입니다.

모든 이동 명령은 opcode에 따라 피연산자 필드의 비트를 왼쪽 또는 오른쪽으로 이동합니다. 모든 시프트 명령은 동일한 구조(복사 피연산자, shift_count)를 갖습니다.

이동할 비트 수(counter_shifts)는 두 번째 피연산자 위치에 있으며 두 가지 방법으로 설정할 수 있습니다.

1) 직접 피연산자를 사용하여 고정 값을 설정하는 것을 포함하는 정적으로;

2) 동적으로, 이는 시프트 명령을 실행하기 전에 시프트 카운터의 값을 cl 레지스터에 입력하는 것을 의미합니다.

cl 레지스터의 차원을 기반으로 시프트 카운터의 값은 0에서 255까지 범위가 될 수 있음이 분명합니다. 그러나 사실 이것은 전적으로 사실이 아닙니다. 최적화 목적을 위해 마이크로프로세서는 카운터의 최하위 0비트 값만 허용합니다. 즉, 값은 31에서 XNUMX 사이의 범위에 있습니다.

모든 시프트 명령은 캐리 플래그 cf를 설정합니다.

비트가 피연산자 밖으로 이동하면 먼저 캐리 플래그에 도달하여 피연산자 외부의 다음 비트 값과 동일하게 설정합니다. 이 비트가 다음에 가는 위치는 시프트 명령의 유형과 프로그램 알고리즘에 따라 다릅니다.

시프트 명령은 작동 원리에 따라 두 가지 유형으로 나눌 수 있습니다.

1) 선형 이동 명령;

2) 순환 시프트 명령.

선형 이동 명령

이 유형의 명령에는 다음 알고리즘에 따라 이동하는 명령이 포함됩니다.

1) 푸시되는 다음 비트는 CF 플래그를 설정합니다.

2) 다른 끝에서 피연산자에 입력된 비트의 값은 0입니다.

3) 다음 비트가 시프트되면 CF 플래그로 들어가고 이전 시프트 비트의 값은 손실됩니다! 선형 이동 명령은 두 가지 하위 유형으로 나뉩니다.

1) 논리적 선형 시프트 명령;

2) 산술 선형 시프트 명령.

논리적 선형 이동 명령에는 다음이 포함됩니다.

1) shl 피연산자, counter_shifts(논리적 왼쪽 시프트) - 왼쪽으로 논리적 시프트. 피연산자의 내용은 shift_count에서 지정한 비트 수만큼 왼쪽으로 이동합니다. 오른쪽(최하위 비트 위치)에는 XNUMX이 입력됩니다.

2) shr 피연산자, shift_count(논리적 오른쪽으로 이동) - 오른쪽으로 논리적 이동. 피연산자의 내용은 shift_count에서 지정한 비트 수만큼 오른쪽으로 이동합니다. 왼쪽(가장 중요한 부호 비트 위치)에는 XNUMX이 입력됩니다.

그림 30은 이러한 명령이 작동하는 방식을 보여줍니다.

쌀. 30. 선형 논리 시프트 명령 작업 방식

산술 선형 시프트 명령은 피연산자의 부호 비트에서 특별한 방식으로 작동한다는 점에서 논리적 시프트 명령과 다릅니다.

1) sal 피연산자, shift_counter(Shift Arithmetic Left) - 왼쪽으로 산술 시프트. 피연산자의 내용은 shift_count에서 지정한 비트 수만큼 왼쪽으로 이동합니다. 오른쪽(최하위 비트 위치)에는 XNUMX이 입력됩니다. sal 명령은 부호를 보존하지 않지만 다음 비트 진행으로 부호가 변경되는 경우 /로 플래그를 설정합니다. 그렇지 않으면 sal 명령은 shl 명령과 완전히 동일합니다.

2) sar 피연산자, shift_count(오른쪽으로 산술 시프트) - 오른쪽으로 산술 시프트. 피연산자의 내용은 shift_count에서 지정한 비트 수만큼 오른쪽으로 이동합니다. 왼쪽의 피연산자에 XNUMX이 삽입됩니다. sar 명령은 부호를 보존하고 각 비트 이동 후에 복원합니다.

그림 31은 선형 산술 시프트 명령이 작동하는 방식을 보여줍니다.

쌀. 31. 선형 산술 시프트 명령의 작동 방식

회전 명령

순환 시프트 명령어에는 시프트된 비트의 값을 저장하는 명령어가 포함됩니다. 순환 시프트 명령에는 두 가지 유형이 있습니다.

1) 간단한 순환 시프트 명령;

2) 캐리 플래그를 통한 순환 시프트 명령 cf.

간단한 순환 시프트 명령에는 다음이 포함됩니다.

1) rol 피연산자, shift_counter(왼쪽으로 회전) - 왼쪽으로 주기적 이동. 피연산자의 내용은 shift_count 피연산자가 지정한 비트 수만큼 왼쪽으로 이동합니다. 왼쪽으로 이동된 비트는 오른쪽에서 동일한 피연산자에 기록됩니다.

2) gog 피연산자, counter_shifts(오른쪽으로 회전) - 오른쪽으로 주기적 이동. 피연산자의 내용은 shift_count 피연산자가 지정한 비트 수만큼 오른쪽으로 이동합니다. 오른쪽으로 이동된 비트는 왼쪽의 동일한 피연산자에 기록됩니다.

쌀. 32. 단순 순환 시프트 명령의 작동 방식

그림 32에서 볼 수 있듯이 작업 과정에서 간단한 순환 시프트 명령은 하나의 유용한 작업을 수행합니다. 즉, 순환 시프트된 비트는 다른 쪽 끝에서 피연산자로 푸시될 뿐만 아니라 동시에 값은 CE 플래그의 값이 됩니다.

캐리 플래그 CF를 통한 순환 시프트 명령은 시프트된 비트가 다른 쪽 끝에서 피연산자에 즉시 입력되지 않고 먼저 캐리 플래그 CE에 기록된다는 점에서 단순 순환 시프트 명령과 다릅니다. 이 시프트 명령( 루프에서 실행되는 경우) 이전에 진행된 비트가 피연산자의 다른 끝에 배치되도록 합니다(그림 33).

다음은 캐리 플래그를 통한 순환 시프트 명령과 관련이 있습니다.

1) rcl 피연산자, shift_count(왼쪽 캐리를 통한 회전) - 캐리를 통한 순환 왼쪽 시프트.

피연산자의 내용은 shift_count 피연산자가 지정한 비트 수만큼 왼쪽으로 이동합니다. 시프트된 비트는 차례로 캐리 플래그 cf의 값이 됩니다.

2) rsg 피연산자, shift_count(오른쪽 캐리를 통해 회전) - 캐리를 통해 오른쪽으로 순환 시프트합니다.

피연산자의 내용은 shift_count 피연산자가 지정한 비트 수만큼 오른쪽으로 이동합니다. 시프트된 비트는 차례로 캐리 플래그 CF의 값이 됩니다.

쌀. 33. 캐리 플래그 CF를 통한 명령어 회전

그림 33은 캐리 플래그를 통해 이동할 때 특히 주기적으로 이동된 비트, 특히 비트 시퀀스의 불일치를 대체할 수 있는 중간 요소가 나타나는 것을 보여줍니다.

이하에서, 비트 시퀀스의 불일치는 이 시퀀스의 필요한 부분을 어떤 식으로든 현지화하고 추출하여 다른 곳에 쓸 수 있게 하는 행위를 의미한다.

추가 시프트 명령

i80386부터 시작하는 최신 Intel 마이크로프로세서 모델의 명령 시스템에는 앞에서 설명한 기능을 확장하는 추가 Shift 명령이 포함되어 있습니다. 배정밀도 시프트 명령은 다음과 같습니다.

1) shld Operand_1, Operand_2, Shift_counter - 이중 정밀도 왼쪽 시프트. shld 명령은 그림 1의 다이어그램에 따라 Operand_2의 비트를 왼쪽으로 이동하고 오른쪽의 비트를 Operand_34에서 대체된 비트 값으로 채워 교체를 수행합니다. 0. 이동될 비트 수는 31~2 범위의 Shift_counter 값에 의해 결정됩니다. 이 값은 즉시 피연산자로 지정되거나 cl 레지스터에 포함될 수 있습니다. Operand_XNUMX의 값은 변경되지 않습니다.

쌀. 34. shld 명령의 체계

2) shrd Operand_1, Operand_2, Shift_counter - 이중 정밀도 오른쪽 이동. 명령어는 Operand_1 피연산자의 비트를 오른쪽으로 이동하고 그림 2의 다이어그램에 따라 왼쪽의 비트를 Operand_35에서 대체된 비트 값으로 채워 교체를 수행합니다. 이동될 비트 수는 다음과 같습니다. 0...31 범위에 있을 수 있는 Shift_counter의 값에 의해 결정됩니다. 이 값은 직접 피연산자에 의해 지정되거나 cl 레지스터에 포함될 수 있습니다. Operand_2의 값은 변경되지 않습니다.

쌀. 35. shrd 명령 체계

우리가 언급했듯이 shld 및 shrd 명령은 최대 32비트까지 이동하지만 피연산자 지정 및 연산 알고리즘의 특성으로 인해 이러한 명령을 사용하여 최대 64비트 길이의 필드를 사용할 수 있습니다.

2. 제어 전송 명령

우리는 프로그램의 선형 섹션이 형성되는 몇 가지 명령에 대해 알게 되었습니다. 이들 각각은 일반적으로 데이터를 변환하거나 전송하기 위한 작업을 수행한 후 마이크로프로세서가 다음 명령으로 제어를 전송합니다. 그러나 그렇게 일관된 방식으로 작동하는 프로그램은 거의 없습니다. 일반적으로 프로그램에는 다음에 실행할 명령을 결정해야 하는 지점이 있습니다. 이 솔루션은 다음과 같습니다.

1) 무조건 - 이 시점에서 다음에 오는 명령이 아니라 현재 명령에서 어느 정도 떨어져 있는 다른 명령으로 제어를 전송해야 합니다.

2) 조건부 - 다음에 실행할 명령에 대한 결정은 일부 조건 또는 데이터 분석을 기반으로 합니다.

프로그램은 일정량의 RAM 공간을 차지하는 일련의 명령 및 데이터입니다. 이 메모리 공간은 연속적이거나 여러 조각으로 구성될 수 있습니다.

다음에 어떤 프로그램 명령을 실행해야 하는지, 마이크로프로세서는 cs의 내용에서 학습합니다. (e) ip 레지스터 쌍:

1) cs - 현재 코드 세그먼트의 물리적(기본) 주소를 포함하는 코드 세그먼트 레지스터.

2) eip/ip - 현재 코드 세그먼트의 시작 부분에 대해 실행될 다음 명령어의 메모리 오프셋을 나타내는 값을 포함하는 명령어 포인터 레지스터.

사용되는 특정 레지스터는 설정된 주소 지정 모드 use16 또는 use32에 따라 다릅니다. use 16이 지정되면 ip가 사용되고 use32가 지정되면 eip가 사용됩니다.

따라서 제어 전송 명령은 cs 및 eip / ip 레지스터의 내용을 변경하며 그 결과 마이크로 프로세서는 다음 프로그램 명령이 아니라 프로그램의 다른 섹션에 있는 명령을 실행하기 위해 선택합니다. 마이크로프로세서 내부의 파이프라인이 재설정됩니다.

작동 원리에 따라 프로그램에서 전환 구성을 제공하는 마이크로 프로세서 명령은 세 그룹으로 나눌 수 있습니다.

1. 제어 명령의 무조건 전송:

1) 무조건 분기 명령;

2) 프로시저를 호출하고 프로시저에서 복귀하라는 명령;

3) 소프트웨어 인터럽트를 호출하고 소프트웨어 인터럽트에서 복귀하는 명령.

2. 조건부 제어 이전 명령:

1) 비교 명령 p의 결과에 의한 점프 명령;

2) 특정 플래그의 상태에 따라 명령을 전환합니다.

3) esx/cx 레지스터의 내용을 건너뛰기 위한 명령.

3. 주기 제어 명령:

1) 카운터 ехх/сх로 사이클을 구성하기 위한 명령;

2) 추가 조건에 의해 주기에서 일찍 종료될 가능성이 있는 카운터 ех/сх로 주기를 구성하는 명령.

무조건 점프

이전 논의에서 전환 메커니즘에 대한 몇 가지 세부 정보가 공개되었습니다. 점프 명령어는 eip/ip 명령어 포인터 레지스터와 가능하면 cs 코드 세그먼트 레지스터를 수정합니다. 정확히 수정해야 하는 사항은 다음에 따라 다릅니다.

1) 무조건 분기 명령의 피연산자 유형(근거리 또는 원거리)

2) (점프 명령에서) 점프 주소 앞에 수정자를 지정하는 것에서; 이 경우 점프 주소 자체는 명령어에 직접 위치하거나(직접 점프) 레지스터나 메모리 셀에 위치할 수 있습니다(간접 점프).

수정자는 다음 값을 사용할 수 있습니다.

1) ptr 근처 - 현재 코드 세그먼트 내부의 레이블로 직접 전환. eip/ip 레지스터만 (지정된 use16 또는 use32 코드 세그먼트 유형에 따라) 명령에 지정된 주소(레이블) 또는 값 추출 기호 - $를 사용하는 표현식을 기반으로 수정됩니다.

2) far ptr - 다른 코드 세그먼트의 레이블로 직접 전환. 점프 주소는 직접 피연산자 또는 주소(레이블)로 지정되며 각각 cs 및 ip/eip 레지스터에 로드되는 16비트 선택기와 16/32비트 오프셋으로 구성됩니다.

3) 단어 ptr - 현재 코드 세그먼트 내부의 레이블로 간접적으로 전환합니다. eip/ip만 수정됩니다(명령에 지정된 주소의 메모리 또는 레지스터의 오프셋 값에 의해). 오프셋 크기 16 또는 32비트,

4) dword ptr - 다른 코드 세그먼트의 레이블로 간접적으로 전환합니다. 두 레지스터(cs 및 eip / ip)는 모두 수정됩니다(메모리의 값으로 - 메모리에서만, 레지스터에서). 이 주소의 첫 번째 단어/dword는 오프셋을 나타내며 ip/eip에 로드됩니다. 두 번째/세 번째 단어는 cs에 로드됩니다. jmp 무조건 점프 명령어

무조건 점프에 대한 명령 구문은 jmp [modifier] jump_address입니다. 반환 지점에 대한 정보를 저장하지 않고 무조건 점프합니다.

Jump_address는 레이블 형태의 주소 또는 점프 포인터가 위치한 메모리 영역의 주소입니다.

전체적으로 마이크로 프로세서 명령 시스템에는 무조건 점프 jmp에 대한 기계 명령의 여러 코드가 있습니다.

이들의 차이점은 전환 거리와 대상 주소가 지정되는 방식에 따라 결정됩니다. 점프 거리는 jump_address 피연산자의 위치에 따라 결정됩니다. 이 주소는 현재 코드 세그먼트 또는 일부 다른 세그먼트에 있을 수 있습니다. 첫 번째 경우 전환을 세그먼트 내 또는 닫기, 두 번째 세그먼트 간 또는 먼 곳에서 전환이라고 합니다. 세그먼트 내 점프는 eip/ip 레지스터의 내용만 변경된다고 가정합니다.

jmp 명령의 세그먼트 내 사용을 위한 세 가지 옵션이 있습니다.

1) 스트레이트 쇼트;

2) 직선;

3) 간접.

절차

어셈블리 언어에는 코드 섹션 복제 문제를 해결하는 여러 도구가 있습니다. 여기에는 다음이 포함됩니다.

1) 절차 메커니즘

2) 매크로 어셈블러;

3) 인터럽트 메커니즘.

종종 서브루틴이라고도 하는 절차는 작업을 분해(여러 부분으로 나누기)하는 기본 기능 단위입니다. 프로시저는 특정 하위 작업을 해결하기 위한 명령의 집합이며 상위 수준에서 작업이 호출된 지점에서 제어를 받고 이 지점으로 제어를 반환하는 수단을 가지고 있습니다.

가장 간단한 경우 프로그램은 단일 프로시저로 구성될 수 있습니다. 즉, 프로시저는 잘 구성된 명령 집합으로 정의할 수 있으며, 한 번만 설명하면 필요한 경우 프로그램의 어디에서나 호출할 수 있습니다.

일련의 명령을 어셈블리 언어의 절차로 설명하기 위해 PROC 및 ENDP라는 두 가지 지시문이 사용됩니다.

절차 설명 구문은 다음과 같습니다(그림 36).

쌀. 36. 프로그램의 절차 설명 구문

그림 36은 프로시저 헤더(PROC 지시문)에서 프로시저 이름만 필수임을 보여줍니다. PROC 지시어의 많은 피연산자 중에서 [거리]를 강조 표시해야 합니다. 이 속성은 가깝거나 먼 값을 취할 수 있으며 다른 코드 세그먼트에서 프로시저를 호출할 수 있는 가능성을 나타냅니다. 기본적으로 [distance] 속성은 near로 설정됩니다.

프로시저는 프로그램의 어느 위치에나 배치할 수 있지만 무작위로 제어하지 않는 방식으로 배치할 수 있습니다. 프로시저가 단순히 일반 명령 스트림에 삽입되면 마이크로프로세서는 프로시저의 명령을 이 스트림의 일부로 인식하고 그에 따라 프로시저의 명령을 실행합니다.

조건부 점프

마이크로프로세서에는 18개의 조건부 점프 명령이 있습니다. 이러한 명령을 통해 다음을 확인할 수 있습니다.

1) 부호가 있는 피연산자 사이의 관계("더 큰 - 더 작은")

2) 부호가 없는 피연산자 간의 관계("상위 - 하위");

3) 산술 플래그 ZF, SF, CF, OF, PF(그러나 AF는 아님)의 상태.

조건부 점프 명령의 구문은 다음과 같습니다.

jcc jump_label

보시다시피 모든 명령의 니모닉 코드는 단어 점프(점프)에서 "j"로 시작하며 명령에 의해 분석된 특정 조건을 결정합니다.

jump_label 피연산자의 경우 이 레이블은 현재 코드 세그먼트 내에만 위치할 수 있으며 조건부 점프에서 세그먼트 간 제어 전송은 허용되지 않습니다. 이와 관련하여 무조건 점프 명령의 구문에 있는 수정자에 대해서는 의문의 여지가 없습니다. 마이크로프로세서의 초기 모델(i8086, i80186 및 i80286)에서 조건 분기 명령은 조건 분기 명령 다음 명령에서 -128에서 +127바이트로 짧은 점프만 수행할 수 있었습니다. 마이크로프로세서 모델 80386부터 이 제한이 제거되었지만 보시다시피 현재 코드 세그먼트 내에서만 가능합니다.

제어권을 조건부 점프 명령으로 이전할 위치를 결정하려면 먼저 제어권 이전 결정을 내릴 조건을 형성해야 합니다.

이러한 상태의 원인은 다음과 같습니다.

1) 산술 플래그의 상태를 변경하는 모든 명령

2) 두 피연산자의 값을 비교하는 비교 명령어 p;

3) esx/cx 레지스터의 상태.

cmp 비교 명령

페이지 비교 명령에는 흥미로운 작업 방식이 있습니다. 빼기 명령과 완전히 동일합니다 - 하위 피연산자, operand_2.

하위 명령과 마찬가지로 p 명령은 피연산자를 빼고 플래그를 설정합니다. 하지 않는 유일한 작업은 첫 번째 피연산자 대신 빼기 결과를 쓰는 것입니다.

명령 구문 str - str operand_1, operand_2(비교) - 두 피연산자를 비교하고 비교 결과에 따라 플래그를 설정합니다.

p 명령으로 설정된 플래그는 특수한 조건부 분기 명령으로 분석할 수 있습니다. 살펴보기 전에 이러한 조건부 점프 명령어의 니모닉에 약간 주의를 기울이겠습니다(표 16). 조건부 점프 명령의 이름을 구성할 때 표기법(jcc 명령 이름의 요소, 우리가 지정함)을 이해하면 암기 및 실제 사용이 용이해집니다.

표 16. jcc 명령 이름의 약어 의미 표 17. p Operand_1, Operand_2 명령에 대한 조건부 점프 명령 목록

조건 분기 명령의 여러 니모닉 코드가 동일한 플래그 값에 해당한다는 사실에 놀라지 마십시오(표 17에서 슬래시로 서로 구분됨). 이름의 차이는 마이크로프로세서 개발자가 특정 명령 그룹과 함께 조건부 점프 명령을 더 쉽게 사용할 수 있기를 바라는 데 있습니다. 따라서 다른 이름은 오히려 다른 기능 방향을 반영합니다. 그러나 이러한 명령이 동일한 플래그에 응답한다는 사실은 프로그램에서 절대적으로 동등하고 동일하게 만듭니다. 따라서 표 17에서는 이름이 아니라 응답하는 플래그(조건)의 값으로 그룹화됩니다.

조건부 분기 명령어 및 플래그

일부 조건부 점프 명령의 니모닉 지정은 작동하는 플래그의 이름을 반영하며 다음과 같은 구조를 갖습니다. 첫 번째 문자는 "j"(점프, 점프)이고 두 번째 문자는 플래그 지정 또는 부정 문자 " n" 다음에 플래그 이름이 옵니다. 이 팀 구조는 그 목적을 반영합니다. 문자 "n"이 없으면 플래그 상태가 확인되고 1이면 점프 레이블로 전환됩니다. 문자 "n"이 있으면 플래그 상태가 0과 같은지 확인하고 성공하면 점프 레이블로 점프합니다.

명령 니모닉, 플래그 이름 및 점프 조건은 표 18에 나와 있습니다. 이러한 명령은 지정된 플래그를 수정하는 명령 뒤에 사용할 수 있습니다.

표 18. 조건부 점프 명령 및 플래그

표 17과 18을 자세히 살펴보면 둘 다 동일한 플래그의 분석을 기반으로 하기 때문에 조건부 점프 명령 중 많은 부분이 동일하다는 것을 알 수 있습니다.

조건부 점프 명령 및 esx/cx 레지스터

마이크로프로세서의 아키텍처에는 많은 레지스터의 특정 사용이 포함됩니다. 예를 들어 EAX/AX/AL 레지스터는 누산기로 사용되고 BP, SP 레지스터는 스택 작업에 ​​사용됩니다. ECX / CX 레지스터에는 특정 기능적 목적도 있습니다. 루프 제어 명령에서 그리고 문자열로 작업할 때 카운터 역할을 합니다. 기능적으로 esx/cx 레지스터와 관련된 조건부 분기 명령이 이 명령 그룹에 더 정확하게 귀속될 수 있습니다.

이 조건부 분기 명령어의 구문은 다음과 같습니다.

1) jcxz jump_label(ex가 XNUMX이면 점프) - cx가 XNUMX이면 점프;

2) jecxz jump_label (점프 Equal ех XNUMX) - ех가 XNUMX이면 점프합니다.

이러한 명령은 루핑할 때와 문자열로 작업할 때 매우 유용합니다.

jcxz/jecxz 명령에 내재된 제한이 있다는 점에 유의해야 합니다. 다른 조건부 전송 명령어와 달리 jcxz/jecxz 명령어는 뒤에 오는 명령어에서 짧은 점프 -128바이트 또는 +127바이트만 주소 지정할 수 있습니다.

주기의 구성

아시다시피이주기는 프로그램을 사용하지 않고는 할 수없는 중요한 알고리즘 구조입니다. 예를 들어 제어 명령의 조건부 전송 또는 무조건 점프 명령 jmp를 사용하여 프로그램의 특정 섹션의 주기적 실행을 구성할 수 있습니다. 이러한 주기 조직을 사용하면 조직의 모든 작업이 수동으로 수행됩니다. 그러나 사이클과 같은 알고리즘 요소의 중요성을 감안할 때 마이크로 프로세서 개발자는 사이클 프로그래밍을 용이하게 하는 명령 시스템에 세 가지 명령 그룹을 도입했습니다. 이러한 명령은 esx/cx 레지스터를 루프 카운터로도 사용합니다.

이러한 명령에 대해 간략하게 설명하겠습니다.

1) 루프 전환_라벨(Loop) - 사이클을 반복합니다. 이 명령을 사용하면 루프 카운터를 자동으로 감소시켜 고급 언어의 for 루프와 유사한 루프를 구성할 수 있습니다. 팀의 임무는 다음을 수행하는 것입니다.

a) ECX/CX 레지스터의 감소;

b) ECX/CX 레지스터를 0과 비교: (ECX/CX) = XNUMX이면 루프 후 다음 명령으로 제어가 전송됩니다.

2) 루프/loopz jump_label

loope 및 loopz 명령은 절대 동의어입니다. 명령 작업은 다음 작업을 수행하는 것입니다.

a) ECX/CX 레지스터의 감소;

b) ECX/CX 레지스터를 XNUMX과 비교;

c) (ECX/CX) = 0 또는 XF = 0인 경우 제로 플래그 ZF의 상태 분석, 제어는 루프 후 다음 명령으로 전송됩니다.

3) loopne/loopnz jump_label

loopne 및 loopnz 명령도 절대 동의어입니다. 명령 작업은 다음 작업을 수행하는 것입니다.

a) ECX/CX 레지스터의 감소;

b) ECX/CX 레지스터를 XNUMX과 비교;

c) 제로 플래그 ZF의 상태 분석: (ECX/CX) = 0 또는 ZF = 1이면 루프 후 다음 명령으로 제어가 전송됩니다.

loope/loopz 및 loopne/loopnz 명령은 상호 작용합니다. zf 플래그를 추가로 구문 분석하여 루프 명령의 작업을 확장하여 이 플래그를 표시기로 사용하여 루프에서 조기 종료를 구성할 수 있습니다.

루프 명령 loop, loope/loopz 및 loopne/loopnz의 단점은 짧은 점프(-128에서 +127바이트)만 구현한다는 것입니다. 긴 루프로 작업하려면 조건부 점프와 jmp 명령을 사용해야 하므로 루프를 구성하는 두 가지 방법을 마스터하십시오.

저자: Tsvetkova A.V.

흥미로운 기사를 추천합니다 섹션 강의 노트, 치트 시트:

관리. 어린이 침대

러시아어와 언어 문화. 어린이 침대

내분비학. 강의 노트

다른 기사 보기 섹션 강의 노트, 치트 시트.

읽고 쓰기 유용한 이 기사에 대한 의견.

<< 뒤로

과학 기술의 최신 뉴스, 새로운 전자 제품:

세계 최고 높이 천문대 개관 04.05.2024

우주와 그 신비를 탐험하는 것은 전 세계 천문학자들의 관심을 끄는 과제입니다. 도시의 빛 공해에서 멀리 떨어진 높은 산의 신선한 공기 속에서 별과 행성은 자신의 비밀을 더욱 선명하게 드러냅니다. 세계 최고 높이의 천문대인 도쿄대학 아타카마 천문대가 개관하면서 천문학 역사의 새로운 페이지가 열렸습니다. 해발 5640m 고도에 위치한 아타카마 천문대는 우주 연구에서 천문학자들에게 새로운 기회를 열어줍니다. 이 장소는 지상 망원경의 가장 높은 위치가 되었으며, 연구자에게 우주의 적외선을 연구하기 위한 독특한 도구를 제공합니다. 고도가 높아서 하늘이 더 맑고 대기의 간섭이 적지만, 높은 산에 천문대를 짓는 것은 엄청난 어려움과 도전을 안겨줍니다. 그러나 어려움에도 불구하고 새로운 천문대는 천문학자들에게 연구에 대한 광범위한 전망을 열어줍니다. ...>>

기류를 이용한 물체 제어 04.05.2024

로봇 공학의 발전은 다양한 물체의 자동화 및 제어 분야에서 우리에게 새로운 전망을 계속 열어주고 있습니다. 최근 핀란드 과학자들은 기류를 사용하여 휴머노이드 로봇을 제어하는 ​​혁신적인 접근 방식을 제시했습니다. 이 방법은 물체를 조작하는 방식에 혁명을 일으키고 로봇 공학 분야의 새로운 지평을 열 것입니다. 기류를 이용하여 물체를 제어한다는 아이디어는 새로운 것이 아니지만, 최근까지도 이러한 개념을 구현하는 것은 어려운 과제로 남아 있었습니다. 핀란드 연구자들은 로봇이 특수 에어 제트를 '에어 핑거'로 사용하여 물체를 조작할 수 있는 혁신적인 방법을 개발했습니다. 전문가 팀이 개발한 공기 흐름 제어 알고리즘은 공기 흐름 내 물체의 움직임에 대한 철저한 연구를 기반으로 합니다. 특수 모터를 사용하여 수행되는 에어 제트 제어 시스템을 사용하면 물리적인 힘에 의지하지 않고 물체를 조종할 수 있습니다. ...>>

순종 개는 순종 개보다 더 자주 아프지 않습니다. 03.05.2024

애완동물의 건강을 돌보는 것은 모든 개 주인의 삶의 중요한 측면입니다. 그러나 순종견이 잡종견에 비해 질병에 더 취약하다는 일반적인 가정이 있습니다. 텍사스 수의과대학 및 생물의학대학 연구원들이 주도한 새로운 연구는 이 질문에 대한 새로운 관점을 제시합니다. DAP(Dog Aging Project)가 27마리 이상의 반려견을 대상으로 실시한 연구에 따르면 순종견과 잡종견은 일반적으로 다양한 질병을 경험할 가능성이 동등하게 높은 것으로 나타났습니다. 일부 품종은 특정 질병에 더 취약할 수 있지만 전체 진단율은 두 그룹 간에 사실상 동일합니다. 개 노화 프로젝트(Dog Aging Project)의 수석 수의사인 키스 크리비(Keith Creevy) 박사는 특정 개 품종에서 더 흔한 몇 가지 잘 알려진 질병이 있다고 지적하며, 이는 순종 개가 질병에 더 취약하다는 개념을 뒷받침합니다. ...>>

아카이브의 무작위 뉴스

폴리머는 기계적 스트레스를 받으면 색이 변합니다. 27.08.2015

미국 펜실베니아 대학(University of Pennsylvania)의 과학자들이 충격의 힘에 따라 색이 변하는 폴리머를 개발했습니다.

폴리머는 홀로그래픽 리소그래피를 사용하여 생성된 광결정을 기반으로 합니다. 굴절률의 주기적인 변화로 인해 결정은 색상 변화와 함께 변형에 반응합니다.

무엇보다도 작동하는 데 전원이 필요하지 않습니다. 이 기술은 부상 시 군인이나 운동선수를 위한 보호 헬멧을 만드는 데 유용하며 충격이 얼마나 강한지 객관적으로 평가하는 데 도움이 될 것입니다.

헬멧에 적용된 코팅의 색상은 피해자에게 적시에 적절한 양의 의료 지원을 제공 할 수 있으며 헬멧의 무게를 무겁게하지 않을만큼 가볍습니다.

다른 흥미로운 소식:

▪ 포드 글레어 프리 하이빔 헤드라이트 시스템

▪ Infineon IMC100 - 디지털 모터 제어 플랫폼

▪ 아마존은 말라가고 있다

▪ 동물의 세계에서 투표

▪ 임신에 대한 심장 박동

과학 기술 뉴스 피드, 새로운 전자 제품

 

무료 기술 라이브러리의 흥미로운 자료:

▪ 사이트 시각적 환상 섹션. 기사 선택

▪ 기사 수중 펌프용 세탁기 엔진. 홈 마스터를 위한 팁

▪ 기사 원생동물이란 무엇입니까? 자세한 답변

▪ 기사 하위 계층의 구름. 여행 팁

▪ 기사 색상 또는 디지털 명칭으로 도체 식별. 무선 전자 및 전기 공학 백과사전

▪ 기사 전압이 1kV 이상인 오버헤드 전력선. 전선과 케이블의 위치와 그 사이의 거리. 무선 전자 및 전기 공학 백과사전

이 기사에 대한 의견을 남겨주세요:

이름 :


이메일(선택사항):


댓글 :





이 페이지의 모든 언어

홈페이지 | 도서관 | 조항 | 사이트 맵 | 사이트 리뷰

www.diagram.com.ua

www.diagram.com.ua
2000-2024