Z-80으로 간단한 컴퓨터 만들기 7

 5.4. 메모리 쓰기 시험

프로그램 수행시 메모리에 데이터가 잘 써지는지 확인하기 위한 시험을 수행하고, 그 결과를 확인한다. 프로그램은 앞에 작성한 것에서 레지스터 A에 있는 데이터를 메모리의 임의의 어드레스에 쓰도록 다음 한 출을 추가하여 작성한다. 

LOOP     IN A, (0)         : 0번 입력장치에서 데이터를 가져와 reg A에 넣는다. 

    OUT (0), A : reg A의 데이터를 0번 출력장치로 출력한다.

    LD (0010), A : reg A의 데이터를 메모리 어드레스 0010에 써 넣는다.

    JP LOOP         : LOOP 로 Jump. 무한 루프 수행

위의 프로그램을 Machine Language로 변환하면 다음과 같다.

LOOP     IN A, (0)         0000     DB 00

    OUT (0), A 0002     D3 00

    LD (0010), A 0004     32 10 00

    JP LOOP         0007     C3 00 00

        000A

위의 프로그램을 5.3 절에 나타낸 절차에 따라 메모리에 입력하고 프로그램을 수행한다. 메모리 주소를 h’0010으로 했는데 실제 프로그램에서는 h’1000으로 입력한다. 이것은 Z80에서 LSB(Least Significant Byte)부터 입력을 받기 때문에 거꾸로 입력하는 것이다. 이제 BUSRQ 스위치를 on 시켜서 프로그램을 중단시키고 Address 스위치를 h’0010으로 만들어 메모리의 데이터를 확인하여 보자. 마지막으로 입력시킨 데이터 값과 같으면 메모리에 정상적으로 write한 것임을 확인할 수 있다. 메모리 주소를 바꾸어 가면서 여러번 시험을 수행하여 메모리에 데이터 써넣는 것이 정상적으로 수행되는지 확인하여 본다.


5.5. 응용 프로그램 시험

이제 응용프로그램을 작성하여 실행하여보자. 우리가 제작한 보드는 입출력장치가 아주 단순하기 때문에 응용프로그램이 아주 제한적이다. 그래서 여기서는 LED를 bit0부터 bit7까지 순차적으로 on/off 시키는 응용프로그램을 작성하여 본다. 그런데 단순히 LED를 on/off 시키는 프로그램을 작성하여 실행시키면 on/off하는 시간이 너무 빨라서 LED 8개가 모두 켜져 있는 것처럼 보일 것이다. 따라서 on/off 하는 시간 간격을 아주 느리게 하여야 한다. 그래서 시간을 지연시키기 위한 Delay subroutine을 만들고 subroutine을 call 하는 프로그램을 작성하여야 한다. 먼저 subroutine을 살펴보자.

DLY     PUSH AF         : 레지스터 A와 F의 데이터를 Stack으로 옮긴다.

    PUSH BC         : 레지스터 B와 C의 데이터를 Stack으로 옮긴다.

    LD B, #ff                 : 레지스터 B에 h’ff를 넣는다.

DLY1     LD C, #ff                 : 레지스터 C에 h’ff를 넣는다.

DLY2     DEC C         : 레지스터 C 값을 1 감소 시킨다.

    JP NZ, DLY2         : Zero Flag가 0이 아니면 DLY2로 Jump 한다.

    DEC B         : 레지스터 B의 값을 1 감소시킨다.

    JP NZ, DLY1         : Zero Flag가 0이 아니면 DLY1로 Jump 한다.

    POP BC         : Stack에 있는 레지스터 B,C의 값으로 B,C를 복원한다.

    POP AF         : Stack에 있는 레지스터 A,F의 값으로 A,F를 복원한다.

    RET         : 원래의 프로그램으로 돌아간다.

Stack에 데이터를 다시 가져 올 때는 Stack에 데이터를 옮긴 순서의 역순으로 가져 와야 한다. 위 프로그램을 Machine Language로 변환하면 다음과 같다. subroutine은 주소 h’00E8에 써 넣을 것이다.

DLY     PUSH AF         : 00E8 F5 ;11

    PUSH BC         : 00E9 C5 ;11

    LD B, #ff                 : 00EA 06 FF ; 7

DLY1     LD C, #ff                 : 00EC 0E FF ; 7

DLY2     DEC C         : 00EE 0D ; 4

    JP NZ, DLY2         : 00EF C2 EE 00 ;10

    DEC B         : 00F2 05 ; 4

    JP NZ, DLY1         : 00F3 C2 EC 00 ;10

    POP BC         : 00F6 C1 ;10

    POP AF         : 00F7 F1 ;10

    RET         : 00F8 C9 ;10

위 프로그램의 맨 뒤 칸에는 각각의 명령어를 수행하는데 필요한 클럭수를 표기하였다. 이를 기반으로 이 프로그램을 수행하는데 필요한 총 클럭수를 계산하여 보면

11+11+7+(7+(4+10)*256+4+10)*256+10+10+10=922939 이다. 따라서 0.5usec*922939≃0.46 이므로 약 0,46초의 지연이 발생한다.

메인 프로그램은 다음과 같다.

    LD SP, #7FFF         : Stack Pointer에 h’7FFF를 써 넣는다,

    LD A, #01         : 레지스터 A에 h’01을 써 넣는다.

LOOP    OUT (00), A         : 레지스터 A 값을 출력한다.

    CALL DLY         : DLY Subroutine을 call 한다.

    RLCA         : 레지스터 A 값을 왼쪽으로 circular rotate 시킨다. 

    JP LOOP                 : LOOP로 Jump한다. 무한 루프

여기서 circular rotate는 레지스터 값을 한비트 왼쪽으로 shift 시키고 최상위 비트를 최하위 비트로 넣는 것을 의미한다. 위 프로그램을 Machine Language로 변환하면 다음과 같다.

    LD SP, #7FFF         : 0000 31 FF 7F

    LD A, #01         : 0003 3E 01

LOOP     OUT (00), A         : 0005 D3 00

    CALL DLY         : 0007 CD E8 00

    RLCA         : 000A 07

    JP LOOP                 : 000B C3 05 00

위 프로그램을 모두 입력하고 수행시키면 LED의 최하위 비트가 On되었다가 순차적으로 왼쪽 LED로 약 0.46초 간격으로 옮아가면서 점등되는 것을 확인할 수 있다. 위의 프로그램의 A번지 값을 h’0F로 변경하면 오른쪽으로 돌아가는 것을 확인할 수 있다. 그리고 이 프로그램에서 맨 처음에 있는 명령어는 SP에 h’7FFF를 써 넣는 것인데 이것은 우리가 갖고 있는 메모리의 맨 뒤 어드레스가 h’7FFF이기 때문에 h’7FFF로 써 넣은 것이고, 메모리가 있는 어드레스를 아무것이나 써 넣어도 된다. 그런데 SP는 Stack에 데이터가 저장(Push)될 때마다 1씩 감소 되고, 데이터를 가져올(Pop) 때마다 1씩 증가하기 때문에 프로그램 영역과는 멀리 띄어 놓는 것이 좋다.

이제 LED를 왼쪽 방향으로 순차적으로 켜지다가 다 켜지면 순차적으로 꺼지다가 최상위 비트만 남으면 다시 오른쪽으로 순차적으로 켜지고, 다 켜지면 오른쪽으로 순차적으로 꺼지는 것을 반복하는 프로그램을 작성하여 보자. 프로그램은 다음과 같다.

MAIN     LD SP, #7FFF     : Stack Pointer에 h’7FFF를 써 넣는다,

    LD A, #01     : 레지스터 A에 h’01을 써넣는다.

LP1     OUT (00), A     : 레지스터 A를 출력한다.

    SLA A     : 레지스터 A를 왼쪽으로 shift 한다.

    ADD A, #01     : 레지스터 A에 1을 더한다.

    CP #FF     : 레지스터 A와 h’FF와 비교한다(A-h’FF)

    JP Z LP2             : 같으면 LP2으로 Jump 한다.

    CALL DLY     : DLY subroutine을 호출한다.

    JP LP1     : LP1으로 Jump 한다.

LP2     OUT (00), A     : 레지스터 A를 출력한다.

    SLA A     : 레지스터 A를 왼쪽으로 shift 한다.

    CP #80     : 레지스터 A와 h’80와 비교한다

    JP Z LP3             : 같으면 LP3으로 Jump 한다.

    CALL DLY     : DLY subroutine을 호출한다.

    JP LP2     : LP2으로 Jump 한다.

LP3     OUT (00), A     : 레지스터 A를 출력한다.

    SRA A     : 레지스터 A를 왼쪽으로 shift 한다. 

    CP #FF     : 레지스터 A와 h’FF와 비교한다

    JP Z LP4             : 같으면 LP4으로 Jump 한다.

    CALL DLY     : DLY subroutine을 호출한다.

    JP LP3     : LP3으로 Jump 한다.

LP4     OUT (00), A     : 레지스터 A를 출력한다.

    SRL A     : 레지스터 A를 왼쪽으로 shift 한다. 

    CP #01     : 레지스터 A와 h’01와 비교한다

    JP Z LP1             : 같으면 LP1으로 Jump 한다.

    CALL DLY     : DLY subroutine을 호출한다.

    JP LP4     : LP4으로 Jump 한다.

위 프로그램을 Machine Language로 변환하면 다음과 같다.

MAIN     LD SP, #7FFF     : 0000 31 FF 7F

    LD A, #01     : 0003 3E 01

LP1     OUT (00), A     : 0005 D3 00

    SLA A     : 0007 CB 27

    ADD A, #01     : 0009 C6 01

    CP #FF     : 000B FE FF

    JP Z LP2             : 000D CA 16 00

    CALL DLY     : 0010 CD E8 00

    JP LP1     : 0013 C3 05 00

LP2     OUT (00), A     : 0016 D3 00

    SLA A     : 0018 CB 27

    CP #80     : 001A FE 80

    JP Z LP3             : 001B CA 24 00

    CALL DLY     : 001E CD E8 00

    JP LP2     : 0021 C3 16 00

LP3     OUT (00), A     : 0024 D3 00

    SRA A     : 0026 CB 2F

    CP #FF     : 0028 FE FF

    JP Z LP4             : 002A CA 33 00

    CALL DLY     : 002D CD E8 00

    JP LP3     : 0030 C3 24 00

LP4     OUT (00), A     : 0033 D3 00

    SRL A     : 0035 CB 3F

    CP #01     : 0038 FE 01

    JP Z LP1             : 003S CA 05 00

    CALL DLY     : 003B CD E8 00

    JP LP4     : 003E C3 33 00


댓글

이 블로그의 인기 게시물