스택을 통한 인자 전달

010. zp를 이용한 인자 전달에서 본 인자 전달 방법 중 2번: 스택을 중간 저장소로 사용하는 방법이다.

스택 메모리를 사용한다는 의미

  • 프로그램 작성 시에 특정할 수 없는 주소가 있음
    • 소형 제어 프로그램에서는 흔하지 않은 경우
  • 하지만 메모리에 접근하는 순서는 알고 있음
    • 그 순서대로 스택 메모리에 전달할 데이터를 넣으면 됨
    • 함수에서는 역순으로 데이터를 꺼내

스택을 사용하여 인자 전달하기 (안 좋은 예)

호출자가 lda #4로 인자를 A 레지스터에 로드한다.

pha로 인자를 스택에 push한다.

  • 스택 $01FF$04가 저장되고, S가 $01FE로 감소한다.

jsr func이 실행되면 회귀 주소를 스택에 push한다.

  • jsr func의 마지막 바이트 주소인 $8008이 스택에 저장된다.

(s[0])

jsr이 자동으로 넣어주는 회귀 주소를 제외하고, 스택의 최상단을 인자로 받는다.

-> A

반환값은 A 레지스터에 저장된다.

<X,P,0x10,0x11>

X 레지스터, 프로세서 상태 레지스터, vtmp, vtmp+1이 변경된다.

pla / sta vtmpjsr이 넣어준 회귀 주소의 Low 바이트를 꺼내 vtmp에 임시 저장한다.

pla / sta vtmp+1High 바이트vtmp+1에 저장한다.

  • 이렇게 해야 그 아래에 있는 함수의 인자에 접근할 수 있다.

pla인자를 A 레지스터에 가져온다.

pha스택을 복구한다.

  • 호출자가 스택을 정리할 수 있도록, 호출 시점의 스택 상태를 그대로 복원하기 위함이다.

s[0] / 2 + 1을 계산한다.

  • lsr로 2를 나누고 adc #1로 1을 더한다.
  • 더하기 전에 clc가 필요하다.

tax로 반환값(계산 결과)을 X 레지스터에 임시 저장한다.

  • 이후 회귀 주소 복구 과정에서 A 레지스터를 사용해야 하기 때문이다.

vtmp에 저장한 회귀 주소를 스택에 복구한다.

  • High 바이트부터 push한다. (lda vtmp+1phalda vtmppha)
  • 스택에 push/pull하는 니모닉이 pha/pla이므로, A 레지스터를 거칠 수밖에 없다.

txa로 X 레지스터에 임시 저장한 반환값을 A 레지스터로 복원한다.

rts로 반환한다.

  • rts 내부 동작에 따라 스택에서 회귀 주소를 pop하여 돌아간다.

호출자로 돌아온 후, pla호출자가 스택에 넣은 인자를 제거한다.

스택을 사용하여 인자 전달하기

위의 안 좋은 예와 같은 동작을 하지만 더 효율적인 코드다.

tsx스택 포인터의 값을 X 레지스터에 가져온다.

  • Page 1의 offset $FC가 저장된다.

lda PG1+3,xs[0] 매개변수를 A 레지스터로 바로 가져온다.

PG1+3의 계산

PG1 + 3 + X = $0100 + $03 + $FC = $01FF 회귀 주소가 스택 2칸을 차지하고, 매개변수가 1개라는 사실을 알기 때문에 offset 3을 미리 지정할 수 있다.

동일하게 s[0] / 2 + 1을 계산한다. (clclsradc #1)

rts로 반환한다.

  • 호출자에서 pla로 스택에 넣은 인자를 제거하는 것은 동일하다.

비교

안 좋은 방법좋은 방법
바이트219
사이클4918

안 좋은 방법은 A 레지스터로만 pha/pla를 해야 하기 때문에 회귀 주소를 빼고 넣는 번거로운 과정이 발생한다.

인자를 여러 번 사용하는 경우

좋은 방법에서 인자를 함수 안에서 여러 번 사용하는 경우, lda PG1+3,x(absolute,X)는 1번만 호출해서 제로 페이지 변수에 저장하고, 이후에는 lda zeropage를 반복 호출하는 것이 효율적이다.