3 분 소요

Programming

Problem Solving

이번 시간에는 Programming이라는 챕터를 배울 예정인데 크게는 Problem Solving과 Debugging으로 나뉜다.

  • Problem Solving
    • ploblem statement를 algorithm으로 변환하고 이를 LC-3 Instruction까지 바꿀 수 있을 때까지 큰 task를 작은 단위의 task로 변환하는 과정을 말한다.
  • Debugging
    • 우리가 의도한 대로 프로그램이 작동하지 않는 경우에 왜 그런지 살펴봐야 하는데, 우리가 배웠던 LC-3 수준에서 Register와 Memory에 하나의 Instruction이 실행될 때마다 의도한 값들이 잘 움직이는지 파악해보는 등의 방법이 있다.

교수님께서는 알고리즘을 짜고 Instruction까지 바꾸는 과정에 공을 들이면 나중에 Debugging 하는 시간을 줄일 수 있다고 하셨다!

Systematic Decomposition

Systematic Decomposition은 stepwise Refinement 라고도 불리는데, Problem Statement로 부터 기계어 수준까지 task를 계속 분해하는 것을 말한다.

problem statement는 자연어기 때문에 이는 모호하거나 불완전한 경우가 많다.

이는 다음 방법으로 해결할 수 있다.

  • 이를 해결하기를 원하는 이에게 물어보거나
  • 의사결정을 내리고 문서화해 놓거나
  • 문제를 변수화해놓거나 실행하는 유저가 선택하도록 한다.

The Three Constructs: Sequential, Conditional ,Iterative

task를 분해할 때 위와 같이 3가지 방법으로 task의 하위 task을 구성할 수 있다.

이 때, 3가지 모두 다 single-entry, single exit이기 때문에 꼭 하나의 방법을 써야하는 것이 아니라 subtask를 또 나눌 때 다른 방법도 적용할 수 있다는 점을 알아두어야 한다.

그리고 Problem statement를 분해할 때 유용한 방법을 알려주셨는데 위 내용을 가지고 problem statment를 단계별 subtask에 대한 설명으로 바꿔나가는 것이다.

위에서 보면 then -> sequential, if -> conditional, for each와 until은 iterative라고 되어있는데 의미를 생각해보면 알 수 있다.

LC-3 Control Instruction to Implement the Three Constructs

그럼 우리가 위에서 배운 기본적인 3가지 contructure를 LC-3 instruction으로 어떻게 표현할 수 있을까? 에 대한 문제를 맞닥뜨리게 된다.

그래서 생각해보면,

  • Sequential: 순차적으로 실행되는 것은 원래 Program Counter에서 다음에 실행할 instruction의 주소를 갖고 있어서 instruction이 실행될 때 원래 순차적으로 실행되기 때문에 이 구조를 표현하기 위해 따로 특별한 instruction은 필요 없다.
  • Conditional and Iterative 같은 경우에는 위 이미지에서 볼 수 있는 것처럼 조건을 충족하는지를 test해야 하는데 이를 instruction으로 표현하기 위해 condition code인 N, Z, P가 필요하다.
    • 위 예시에서는 R0랑 R1이 같은가? 가 조건이라면 이는 CODE로 R0 - R1 연산을 해서 0(Zero)가 나오는지 보면 될 것이고 Z가 1이 맞다면 Jump Instruction을 사용해 다음 instruction으로 넘어가게 하면 될 것이다.

예시로 Conditional Structure를 구현하기 위해 Instruction으로 표현하는 것을 보자.

A 주소는 조건에 대한 Instruction 값을 가지고 있고 B의 경우 조건이 충족되지 않으면 address C에 대한 PCoffset을 표현하여 Subtask 1에 대한 Instruction을 수행하도록 한다. 그리고 순차적으로 다음 subtask를 수행하면되고 조건이 충족되면 111로 nzp를 표현해놨으므로 D로 JUMP하도록 설정되어있는 것이다. 이렇게 우리가 조건이 True면 a하고 false면 b하는 것을 기계어 수준에서 표현되는 것을 알 수 있다.

그리고 Iterative 예시도 보게 되면, 먼저 test할 condition을 만들고 test condition이 _Not Test Condition_이게 되면 C로 이동하게 하여 loop를 빠져나가게 만들고 아니면 B가 실행되고 다시 A로 돌아가 조건을 충족했는지 평가하게 만든다.

Debugging

디버깅은 우리가 작성한 프로그램이 의도대로 작동이 안될 때 무엇이 문제인지 찾는 과정을 의미한다.

강의에서는 예시로 우리가 길을 잃어버렸을 때 어떻게 찾는 지에 대한 얘기를 하는데, 그냥 아무데나 가보거나, 아는 지점으로 돌아가던지 또는 지도를 보는 등의 행위가 있겠다.

이런 지도를 보는 행위가 같은 행동이 디버깅에서는 프로그램을 tracing하는 것이다. 즉,

  • instruction의 실행되는 순서를 검사해본다던지
  • 만들어지는 결과를 추적한다던지
  • 각 instruction마다 우리가 기대한 결과와 실제 결과를 비교해보는 등이 있다.

Debugging Operations

기계어 수준에서 디버깅을 할 때는,

  1. memory와 register의 value를 보고
  2. 또 값을 store하거나
  3. 프로그램에서 instruction 순서에 따라 실행해보고
  4. 우리가 필요할 때 실행을 멈춰야 한다.

디버깅을 하기 위해서 우리가 흔히 맞닥뜨리는 오류를 살펴보자.

  1. Syntax Error
    • 이 경우에는 보통 기계어 수준에서는 잘 없고 고수준 언어에서 컴파일러가 자연어를 기계어로 번역할 때 오류를 잡아서 알려주는 경우가 종종 있다.
  2. Logic Error
    • 우리가 생각한 대로 프로그램을 만들었으나 의도대로 작동하지 않는 경우 무엇이 잘못됐는지 들여다봐야 한다.
  3. Data Error
    • 2번과 1번의 경우 내가 많이 접해본 에런데 이 부분이 조금 생소했어서 이런 경우를 생각해서도 코드를 작성해야겠구나 했다. 즉, input data가 우리가 기대한 것과 다를 때 발생하는데, 다양한 input data과 함께 program을 테스트 해볼 것을 권한다.

우리가 디버깅하기 위해서는 program을 tracing해야 되는데, 프로그램을 한 단위마다 실행하면서 레지스터와 메모리의 값들을 확인하는 것을 말한다.

여기서 유용한 방법 3가지를 소개하는데 다음과 같다.

  1. single-stepping
    • instruction 하나마다 실행해보며 레지스터와 메모리에 값이 제대로 들어있는지 확인해보는 것을 말한다.
  2. breakpoint
    • 1번이 익숙해지면 내가 어느 부분에서 instruction이 제대로 돌아가는 것을 알고 특정 instruction을 실행했을 때 값이 어떻게 나올지 확인해보는 용도로 breakpoint를 사용할 수 있다.
  3. watchpoint
    • instruction이 아니라 레지스터나 메모리의 location이 변경됐을 때 특정 값이 동일한지 확인하기 위해 사용하는 방법이다.

이러한 simulator를 통해 디버깅을 할 수 있다고 하는데, GPR과 PC, IR 그리고 각 instruction 별 주소값과 비트 패턴, 어떤 opcode와 operand가 있는지 볼 수 있는 게 신기했다.

지금 당장은 아니더라도 나중에 2회독으로 돌아왔을 때 한 번 써봐야겠다.

태그: ,

카테고리:

업데이트:

댓글남기기