2. 196을 배워보자
I. 196의 내부 구조
1. 196의 특징
20MHz operation
RALU
488 byte internal RAM
28개 interrupt source, 16개 interrupt vector
1.4us의 16bit X 16bit multiplication (at 20MHz)
Power down mode, Idle mode
16bit watchdog timer
full duplex serial port
동적 구조의 8bit/16bit external BUS width
Capture 기능이 있는 16bit up/down counter
8/10bit ADC with Sample/Hold
232 byte register file
Register-Register Architecture
PTS(Peripheral Transaction Server)
HOLD/HOLDA protocol
5개의 8bit I/O port
3 PWM output
네 개의 16bit software timer
2. 196의 내부 구조
(1) 196KC의 memory 구조
196KC의 memory map을 살펴보면 다음과 같다.
external Memory/IO |
FFFFH ~ 4000H |
|
internal ROM/EP-ROM or external ROM |
3FFFH ~ 2080H |
|
reserved |
||
상위 8개의 interrupt vector table |
203FH ~ 2030H |
|
EP-ROM security key |
202FH ~ 2020H |
|
reserved |
||
CCB(Chip Configuration Byte) |
2018H |
|
reserved |
||
하위 8개의 interrupt vector table |
2013H ~ 2000H |
|
port 3, 4 |
1FFFH 1FFEH |
Address/Data BUS |
external memory 또는 IO |
1FFDH ~ 0200H |
|
internal data memory, register file(SP, RAM, SFR, external program code memory) |
01FFH ~ 0000H |
80C196KC internal RAM & SFR |
|
여기에서 내부 RAM 중 Register file에 대해 간단히 알아보자.
내부 RAM의 memory map
상위 RAM |
01FFH ~ 0100H |
window를 이용하여 직접 번지 지정으로 data access |
Register File |
00FFH ~ 001AH |
direct, indirect, index addressing 등을 이용하여 data access |
SP(Stack Pointer) |
0019H ~ 0018H |
Stack Pointer로 사용 |
SFR(Special Function Register) |
0017H ~ 0000H |
I/O 및 다른 주변 기능을 제어 |
|
1) Register File
80196은 Register File을 base로 하여 연산을 하는 Register to Register Architecture로 되어있다. register file은 232byte의 내부 RAM(18H~0FFH)을 모두 register로 사용할 수 있으며, 196KC에는 256byte의 추가 RAM을 가지고 있어서 수직 window를 이용하여 사용할 수 있다.
18H~0FFH의 영역을 register file이라고 부르는데, 이 영역은 byte(8bit), word(16bit), long word(32bit)로 access할 수 있다. 이 register file은 RALU(Register/Arithmetic Logic Unit)가 사용하므로 232개의 Accumulator가 있는 것과 같은 효과를 낸다. 즉, 특정 Accumulator를 이용하여 연산을 할 필요가 없으므로 프로그램에 유용하다.
한편, 상위 RAM은 SFR의 window를 이용하여 0100H~01FFH의 256byte의 RAM을 register file로 mapping 시켜서 RALU에서 사용할 수 있다.
2) Stack Pointer
이 곳은 16bit Stack Pointer로 사용되는 부분이다. 사용자가 초기 설정하여 사용한다.
3) SFR(Special Function Registers) & 수평 window
196의 IO를 제어하기 위한 Register이다.SFR을 알아야만 controller에서 IO를 마음대로 휘어잡을 수 있다.
또 196에서는 수평 window를 이용하여 다른 기능의 SFR을 선택할 수가 있다. 아래의 표는 196의 SFR이다.
address |
수평 window 0 Read |
수평 window 0 Write |
수평 window 1 Read/Write |
수평 window 15 Read |
수평 window 15 Write |
17H |
IOS2 |
PWMO_CONTROL |
PWM2_CONTROL |
PWM0_CONTROL |
IOS2 |
16H |
IOS1 |
IOC1 |
PWM1_CONTROL |
IOC1 |
IOS1 |
15H |
IOS0 |
IOC0 |
reserved |
IOC0 |
IOS0 |
14H |
WSR |
WSR |
WSR |
WSR |
WSR |
13H |
INT_MASK1 |
INT_MASK1 |
INT_MASK1 |
INT_MASK1 |
INT_MASK1 |
12H |
INT_PEND1 |
INT_PEND1 |
INT_PEND1 |
INT_PEND1 |
INT_PEND1 |
11H |
SP_STAT |
SP_CON |
reserved |
SP_CON |
SP_STAT |
10H |
PORT2 |
PORT2 |
reserved |
reserved |
reserved |
0FH |
PORT1 |
PORT1 |
reserved |
reserved |
reserved |
0EH |
PORT0 |
BAUD_RATE |
reserved |
reserved |
reserved |
0DH |
TIMER2(HI) |
TIMER2(HI) |
reserved |
T2CAPTURE(HI) |
T2CAPTURE(HI) |
0CH |
TIMER2(LO) |
TIMER1(LO) |
IOC3 |
T2CAPTURE(LO) |
T2CAPTURE(LO) |
0BH |
TIMER1(HI) |
IOC2 |
reserved |
IOC2 |
TIMER1(HI) |
0AH |
TIMER1(LO) |
WATCHDOG |
reserved |
WATCHDOG |
TIMER1(LO) |
09H |
INT_PEND |
INT_PEND |
INT_PEND |
INT_PEND |
INT_PEND |
08H |
INT_MASK |
INT_MASK |
INT_MASK |
INT_MASK |
INT_MASK |
07H |
SBUF(Rx) |
SBUF(Tx) |
PTSSRV(HI) |
SBUF(Tx) |
SBUF(Rx) |
06H |
HSI_STATUS |
HSO_COMMAND |
PTSSRV(LO) |
HSO_COMMAND |
HSI_STATUS |
05H |
HSI_TIME(HI) |
HSO_TIME(HI) |
PTSSEL(HI) |
HSO_TIME(HI) |
HSI_TIME(HI) |
04H |
HSI_TIME(LO) |
HSO_TIME(LO) |
PTSSEL(LO) |
HSO_TIME(LO) |
HSI_TIME(LO) |
03H |
AD_RESULT(HI) |
HSI_MODE |
AD_TIME |
HSI_MODE |
AD_RESULT(HI) |
02H |
AD_RESULT(LO) |
AD_COMMAND |
reserved |
AD_COMMAND |
AD_RESULT(LO) |
01H |
ZERO_REG(HI) |
ZERO_REG(HI) |
ZERO_REG(HI) |
ZERO_REG(HI) |
ZERO_REG(HI) |
00H |
ZERO_REG(LO) |
ZERO_REG(LO) |
ZERO_REG(LO) |
ZERO_REG(LO) |
ZERO_REG(LO) |
|
* WSR (Window Select Register)을 이용해서 수평 window를 선택하는 경우
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
function |
X |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
수평 window 0 |
X |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
수평 window 1 |
X |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
수평 window 15 |
|
위의 SFR들을 보면 같은 address에 서로 다른 기능의 Register가 mapping 되어 있는데, 이는 96 계열 controller의 이전 버전과의 호환성 때문에 추가되면서 발생한 것이다. 따라서 특정 register를 선택하기 위해서 WSR을 이용하여 window를 선택하고 register에 access하는 것이다. 또 읽고 쓰는 경우에 따라서도 access되는 address가 달라지므로 주의해야 한다. 이는 고급 사용자에게 매우 중요하다. 자칫하면 실수를 찾을 수 없기 때문이다.
(2) RALU
보통 많은 register가 없는 CPU에서는 연산을 위해서 memory로부터 Accumulator로 data를 가져와야 한다. 그러나 196에서는 변수가 내부 RAM(register)에 있을 경우에는 그럴 필요가 없다. 즉, register가 accumulator의 역할을 한다.
예를 들어 덧셈을 하는 경우 보통의 CPU에서는
MOV AX, 40H
MOV BX, 50H
ADD AX, BX
MOV 40H, AX
라고 해야하지만, 196KC에서는
ADD 40H, 50H
라는 한 줄로 끝나게 된다. (40H와 50H가 내부 변수라면)
또한 196은 덧셈과 뺄셈, 곱셈, 논리 연산 명령에 세 개의 operand가 있기 때문에, 예를 들어 60H=40H-50H(40H의 내용에서 50H의 내용을 뺀 후 60H에 그 내용을 저장한다.)의 경우
SUB 60H, 40H, 50H
로 한 줄에 끝낼 수가 있다. 이 때문에 같은 속도의 다른 CPU보다 연산속도가 두 세배 빨라지고 memory 효율은 10~30%정도 좋아질 수 있다.
II. 196 Assember
II-I. Assembler를 시작하기 전에
1. Assembler 기초
어셈블리어는 기본적으로 mnemonic + operand로 한 명령이 구성된다. 예를 들면 다음과 같다.
START: ADD AX, #40H
label mnemonic operands (AX는 variable)
mnemonic이란 명령의 종류를 말하는 것이고, operand란 명령 실행의 대상이 된다. 이렇게 만들어진 한 명령 단위는 Assembler에 의해서 OP code로 변환되어 object file이 만들어진다. 이는 다시 ROM이나 RAM에 들어갈 수 있는 실행 가능한 code(기계어)로 변환되어 실행되게 된다.
operand는 mnemonic에 따라 달라진다. 두 개인 것도 있고, 한 개인 것도 있고, 없는 것도 있다. 또한 이들은 CPU마다 다르다. 그러나 그 기본적인 구조는 비슷하기 때문에 한 종류의 어셈블리어를 알고 있다면 다른 어셈블리어를 배우기는 그리 어렵지 않다.
Programming할 때 Assembler를 반드시 쓰라는 법은 없다. C를 써도 되고 C++을 써도 상관없다. 그러나 Hardware에 접근하기 위해서는 Assembler는 필수이고, 이를 알아야만 hardware를 효율적으로 사용할 수 있고, 최적의 code를 만들 수가 있다. 그러므로 속도를 위한 hardware라면 C보다는 assembler가 더 좋을 것이다.
한편 같은 CPU라도 여러 종류의 assembler가 있을 수 있다. 물론 대부분은 비슷하나 약간씩 다른 점도 있으므로 자신이 어떤 assembler를 사용하는지 알고 사용해야 한다. 예를 들어 8x86계열이라면 MASM(Macro Assembler)이 유명할 것이다.
이후부터는 intel에서 제공하는 asm96을 이용하여 어셈블리어 프로그램 개발과정에 살펴보겠다. (196 assembler는 http://www.postech.ac.kr/downarea/asm/ic96.zip 에서 가져가기 바람.) 아래 그림은 program의 개발 과정이다. source program은 어셈블리어에 의해서 만들어지고 이는 어셈블러에 의해 Assemble된다. 여기서 list file을 얻을 수 있다. 여기에서 만들어진 object file은 oh나 hex2bin등을 통해서 hex file이나 bin file등의 code로 만들어진다.
2. 196 Assembler
우선 일반적인 사항들에 대해서 살펴보자.
(1) 이름(Label, varible,...)으로 사용할 수 있는 문자
모든 ASCII 문자는 사용 가능하고, 대소문자의 구분은 없다. 그러나 이름에서 첫 번째 문자로는 0~9의 숫자를 사용할 수 없고 다음 문자는 제한 없다. 길이는 최대 31문자이다.
(2) 숫자 표기
진수 |
추가 사항 |
example |
2 (binary) |
B (b) |
11000011B |
8 (oxta) |
O, Q (o, q) |
145O |
10 (decimal) |
D, d 혹은 없어도 됨 |
95 |
16 (hexa) |
H, h |
9FH, 0F0H |
|
16진수의 경우 lable이나 이름과 숫자를 구별하기 위해서 처음에 A~F로 시작하는 경우에는 앞에 0을 하나 더 써준다.
(3) 문장
위에서 설명한 것과 같다.
LOOP: ORB TEMP, #'A' ; Temp에 'A'의 ASCII 값 더함
lable mnemoic operands comment
(4) Lable, Variable, Number
1) Lable
Lable은 jump나 call할 때의 subroutime 혹은 address의 이름이며 위에서처럼 뒤에 ':'을 붙인다. 그렇지 않으면 assembler는 lable을 구별할 수 없다.
2) Variable
실제 값의 위치를 나타내기 위해 사용한다.
예) TEMP: DCB 155
TEMP1: DSB 15
(C에서의 변수 선언과 같음 - int temp;)
3) Number
숫자로서 EQU(Equate)나 SET을 사용하여 정의하고 쓴다.
예) RADIUS EQU 450
(C에서 define과 같음)
(5) Asm96 지시어
지시어 |
function |
example |
Module level을 정의하는 지시어 | ||
MODULE |
module에 대한 정보 제공 |
ABC MODULE STACKSIZE(100) |
PUBLIC |
public symbol을 정의 |
ACC |
EXTRN |
외부 참조 symbol을 정의 |
ACD |
END |
end of program |
END |
Location counter를 정의하는 지시어 | ||
CSEG |
code segment 정의 |
CSEG AT 2080H |
DSEG |
data segment 정의 |
DSEG AT 8000H |
RSEG |
overay되지 않는 register segment 정의 |
RSEG AT 0020H |
OSEG |
overay되는 register segment 정의 |
OSEG AT 0100H |
ORG |
시작 address를 정의(Origin) |
ORG 8200H |
Symbol을 정의하는 지시어 | ||
EQU |
symbol 정의 (재정의가 불가능)(Equate) |
ESC EQU 0 |
SET |
symbol 정의 (재정의가 가능) |
BEEP SET 0모 |
Code를 정의하는 지시어 | ||
DCB |
변수를 byte로 정의(Define Code in Byte) |
DCB 33H, 'HI' |
DCW |
변수를 word로 정의(Define Code in Word) |
DCW 4444H |
DCL |
변수를 long word로 정의(Define Code in Long word) |
DCL 12345678H |
DCR |
변수를 실수로 정의(Define Code in real) |
DCR 3.14159 |
조건 어셈블리 지시어 | ||
IF |
조건 어셈블리 블럭 시작 |
|
ELSE0 |
조건 어셈블리 블럭을 대체 |
|
ENDIF |
조건 어셈블리 끝 |
|
|
196 어셈블러는 명령(mnemonic)의 기능에 따라 몇몇 명령군으로 나누어 볼 수가 있다. 이에 따라 나누어 보면 다음과 같다.
데이터 전송 명령
산술 연산 명령
논리 연산 및 shift 명령
branch 명령
Stack과 subroutine 및 system 제어 명령
또 Bit, Byte, Word, Long word에 따라 명령도 약간씩 달라지므로 주의해야 한다.
다음 시간부터는 이 순서에 따라 어셈블리어를 배우도록 하자.
다음은 예제이니 읽어보고 위에서 배운 것들을 참고하여 분석해보자.
(앞쪽 생략)
north equ 40h ; 방향정보
west equ 0
east equ 80h
south equ 0c0h
CSEG AT 8004H ; AD interrupt
ljmp AD_SERVICE
CSEG AT 800CH ; HSO interrupt
ljmp HSO_SERVICE
CSEG AT 8014H ; Software timer interrupt
ljmp SOFT_TIMER
;CSEG AT 801CH ; extint
; LJMP EXTI_SERVICE
CSEG AT 8030H ; Timer2 overflow interrupt
ljmp timer2_service
CSEG AT 8200H
START: LDB IOC0, #00000000B ; all hsi unit is disable, select P2.3, P2.5
LDB IOC1, #01111010B ; HSO4, HSO5 enable, select ACH7 as exint1 input,
; timer2 overflow interrupt enable
LDB IOC2, #10010100B ; Fast mode AD enable, timer2 normal increasing mode
LDB WSR, #1
LDB IOC3, #000000001B ; timer2 clock source internal
CLRB WSR
CLRB IOPORT1 ; port initialize
CLRB IOPORT2
ldb ax, #0ffh ; 벽 정보 초기화
clrb ch
clrdl: stb ch, data_location[ax]
djnzw ax, clrdl
EI
restart: ldb current_dir, #north ; set direction (forward, absolute)
ldb current_xy, #0 ; set current coordinate (x, y = 0, 0)
ldb target_xy, #44h ; set destination