C 언어와 Assembly Language 을사용한 Programming 20011.9 경희대학교조원경 1. AVR Studio 에서사용하는 Assembler AVR Studio에서는 GCC Assembler와 AVR Assmbler를사용한다. A. GCC Assembler : GCC를사용하는경우 (WinAVR 등을사용하는경우 ) 사용할수있다. New Project 를만들때 Project Type Window에서 AVR GCC를선택한다. B. AVR Assembler : AVR Studio를설치하면함께설치된다. New Project 를만들때 Project Type Window에서 Atmel AVR Assembler를선택한다. C. GCC C와 Assembly 언어를혼합한프로그램을작성할경우에는 AVR GCC Project를선택한다.
2. GCC Assembler와 AVR Assembler의차이 A. GCC Assembler 를사용하는경우 i. Assembly Language Program은.S Extension 을갖는다. ii. C Language Program은.c Extension 을갖는다. iii. GCC Assembler는 GCC C/C++ 와같은 Preprocessor을사용한다. #include, #define 등을사용할수있다. iv. Data Segment 에정의된변수를초기화할수있다. Assembler 는 Initialized Data 를초기화하는데필요한 Startup Code 를발생 시키고, 이프로그램에서 Initialized Data 를 SRAM 에 Copy 한다. B. Atmel AVR Assembler 를사용하는경우 i. Assembly Language Program 은.asm Extension 을갖는다. ii. Atmel AVR Assembler는 Data Segment 영역을초기화하지않는다. 만약초기화가필요한경우에는 Code Segment 내에초기화에필요한 Data를포함시키고, 이값을 Data Segment 영역으로복사하여주는초기화프로그램을작성하여실행하여야한다. C. 기타차이 GCC Assembler hi8 lo8.asciz hello.section.data.section.text <avr/io.h> Atmel AVR Assembler high low.db hello,0.dseg.cseg m128def.inc 3. GCC Assembly Language Program 예 // Project Name : sw_led_basic # ifndef atmega128 #define atmega128 # endif
#define FOSC 16000000// Clock Speed #include <avr/io.h> // Definition file for ATmega128 // [Add all hardware information here] // Program Constants // Program Variables Definitions #define RG_TEMP r0 // Temporary Register #define RG_ZERO r1 // #define AC0 r16 // #define AC1 r17 // // SRAM Definitions #define SFR_OFFSET 0.section.data.ORG 0x0000 //cbyte:.byte 0x00 // Define Constant Byte //cword:.word 0x0000 // Define Constant Word //clongword:.long 0x00000000 // Define Constant Long Word.section.text.global main // Main Entry into program main: // I/O Port Init clr AC0 sts PORTF, AC0 // PORTF : Memory Mapped I/O Port
out DDRD, AC0 // PORTD : I/O Maped Inputport ser AC0 out PORTD, AC0 sts DDRF, AC0 // Input Port PullUp // PORTF : Output Port loop:.end in AC0, PIND andi AC0, 0x03 sts PORTF, AC0 rjmp loop // Loop Indefinitly 4. Atmel AVR Assembly Language Program 예.NOLIST.include "m128def.inc" ; Definition file for ATmega128 ; Interrupt Vectors Table.include "avr128_interrupt_table.inc" ; [Add all hardware information here].device ATmega128.LIST ; Program Constants.equ const = $00 ; Generic Constant Structure ; ; Program Variables Definitions.def AC0 = r16 ;.def AC1 = r17 ; ; SRAM Definitions
.DSEG.ORG 0x0100 ;cgbyte:.db $00 ;cgword:.dw $0000 ;vgbyte:.byte 1 ;vgword:.byte 2 ;vgdword:.byte 4 ; Define Constant Byte ; Define Constant Word ; Reserve 1 Byte to Global Variable ; Reserve 2 Byte to Global Variable ; Reserve 4 Byte to Global Variable.CSEG ; Func: RESET Handler Routine RESET: ; Stack Pointer Init ldi AC0, LOW(RAMEND) out SPL, AC0 ldi AC0, HIGH(RAMEND) out SPH, AC0 rjmp MAIN ; Main Entry into program MAIN: ; I/O Port Init clr AC0 sts PORTF, AC0 out DDRD, AC0 ser AC0 out PORTD, AC0 sts DDRF, AC0 ; PORTF : Memory Mapped I/O Port ; PORTD : I/O Maped Inputport ; Input Port PullUp ; PORTF : Output Port LOOP: in AC0, PIND ;
andi AC0, $03 sts PORTF, AC0 rjmp LOOP ; Loop Indefinitly 5. C와 Assembly Language을혼합한 Program에서 Register의사용 C Program에서 Assembly 언어로작성된 Subroutine을 Call 하는경우호환성을위하여 C Compiler에서 Rg를사용하는규칙을아는것이중요하다. A. C Compiler에서 Register 사용 i. r0 : Temporary Register 로사용한다. Assembly 언어로작성된프로그램에서 C 함수를호출하는경우이 Rg는 Assembly Code 내에서 Save 되고, Restore 되어야한다. 그러나, Compiler에의하여생성된 Interrupt Routine은 r0 Rg를 Save 하고 Restore 한다. ii. r1 : Compiler는이 Rg의값이 zero라고가정한다. iii. r2 r17, r28, r29 : 이 Rg 들은 Compiler에의하여생성된 Code 에서임시저장장소로이용되기때문에 Assembly Code에서이 Rg가사용되고, Compiler에의하여생성된 Code에서 Call 된다면이 Rg들은 Assembly Code 내에서 Save 되고, Restore 되어야한다. Y Index Register(r29:r28) 는 Function의 Stack Frame Pointer 로사용된다. iv. r18 r27, r30, r31 : 이 Rg 들도 Assembly Code에서사용되고, Assembly Code에서 Compiler에의하여생성된 Code를 Call 하여야한다면 Assembly Code 내에서 Save 되고, Restore 되어야한다. 6. Function Call을할때 Argument Passing과 Return Values A. Register를이용한 Parameter Passing i. 미리약속된 Rg를사용하여 Function Arguments를 Passing 한다. ii. Function Arguments 는좌측에서우측으로배치되어있고, r25 로 부터 r8 까지할당된다. iii. 모든 Arguments 는짝수개의 Rg 에할당된다. C Compiler 가
MOVW 명령을사용하여 Word 단위로 Data 를처리하도록하여 보다효과적으로데이터를처리할수있다. iv. 만약보다더많은 Arguments 를 Pass 하여야하는경우에는나머 지 Arguments 는 Stack 을이용하여 Pass 한다. v. Rg 를이용한 Arguments Passing 예 Void lcd_cursor_xy(uint8_t x, uint8_t y); lcd_cursor_xy(2,3); // 이함수는아래와같이 Compile 된다. eor r25, r25 ; r25 clear ldi r24, 0x02 eor r23, r23 ; r23 clear ldi r22, 0x03 call lcd_cursor_xy B. Stack을이용한 Parameter Passing i. 일부함수 (Printf, scanf, etc) 는 모든 Arguments를 Stack을이용 하여 Pass 한다. ii. 이때 char parameters는 Integer(Word) 로확장되어 Pass 된다. iii. Parameters는오른쪽에있는 parameter 부터 Stack에 Push 된다. iv. 8-bit 변수는 16-bit로확장되어 Pass 된다. v. 이때상위 8-bit는 zero로 set 된다. C. Return Values i. 8-bit 값은 r24을이용하여 Return 된다. ii. 16-bit 값은 r25:r24을이용하여 Return 된다. iii. 32-bit 값은 r25:r24:r23:r22을이용하여 Return 된다. iv. 64-bit 값은 r25:r24:r23:r22:r21:r20:r19:r18 을이용하여 Return 된다. 7. Assembly Language 로작성된 Program 에서 C 프로그램함수를 Call 하는
경우의예 GCC Assembly 언어로작성된 main 프로그램, 이프로그램에서 GCC c언어로작성된함수를 Call 한다. // Project Name : asm_call_c_func_3v_add // // Author: chowk // Lab: KHU // Date: 2011. 8. 20 // Assembly 프로그램에서 C 로작성된 Subroutine 을 Call 하는예제이다. # ifndef atmega128 #define atmega128 # endif #define FOSC 16000000// Clock Speed #include <avr/io.h> // Definition file for ATmega128 // [Add all hardware information here] // Program Constants // Program Variables Definitions #define RG_TEMP r0 // Temporary Register #define RG_ZERO r1 //
#define AC0 r16 // #define AC1 r17 // // SRAM Definitions #define SFR_OFFSET 0.section.data.ORG 0x0000 // Global 변수 sum을선언하고 0x0000 으로초기화한다. sum:.word 0x0000 // Global 변수 x1을선언하고 0x0104 으로초기화한다. x1:.word 0x0104 // Global 변수 x2을선언하고 0x0205 으로초기화한다. x2:.word 0x0205 // Global 변수 x3을선언하고 0x0306 으로초기화한다. x3:.word 0x0306.section.text.global main // Main Entry into program main: // I/O Port Init clr AC0
sts PORTF, AC0 // PORTF : Memory Mapped I/O Port out DDRD, AC0 // PORTD : I/O Maped Inputport ser AC0 out PORTD, AC0 sts DDRF, AC0 // Input Port PullUp // PORTF : Output Port // Y Pointer 에 Global 변수 x1 의시작번지를 Load 한다. ldi r29, hi8(x1) // Y : r29:r28 ldi r28, lo8(x1) // x1 값을 r25:r24 을이용하여 Subroutne에 Pass 한다. ld r24, Y+ ld r25, Y+ // 일반적으로 x2의 Pointer를다시 Load 하여야하나 // 이 Assembly 프로그램에서는 x2가다음번지에할당된것을알기때문에생략할수있다 // ldi r29, hi8(x2) // ldi r28, lo8(x2). // x2 값을 r23:r22 을이용하여 Subroutne에 Pass 한다. ld r22, Y+ ld r23, Y+ // 일반적으로 x3 의 Pointer 를다시 Load 하여야하나 // 이 Assembly 프로그램에서는 x3 가다음번지에할당된것을알기때 문에생략할수있다. 이경우 2 개의명령을생략할수있다.
// ldi r29, hi8(x3) // ldi r28, lo8(x3) // x3 값을 r21:r20 을이용하여 Subroutne에 Pass 한다. ld r20, Y+ ld r21, Y // c 언어로작성된 Subroutine을호출한다. call add // Y Pointer 에 Global 변수 sum의시작번지를 Load 한다. ldi r29, hi8(sum) ldi r28, lo8(sum) // Subroutine 으로부터 Return 된결과를 sum에저장한다. st Y+, r24 st Y, r25 loop: in AC0, PIND // SW의상태를읽는다. andi AC0, 0x01 // SW0 를테스트한다. breq NEXT1 // SW0 가 Open 된경우 sum의 LSD(8-bit) 를 LED에출력한다. sts PORTF, r24 rjmp NEXT2 // SW0 가 Closed 된경우 sum의 MSD(8-bit) 를 LED에출력한다. NEXT1: sts PORTF, r25 NEXT2: rjmp loop // Loop Indefinitly
.end C 언어로작성된 Subroutine(Fuction) 예 #include <avr/io.h> #include <stdio.h> int short add( int short, int short, int short); // function call 과정 (parameter passing, frame pointer 의저장 ) 의이해하기위함. int short add( int short x1, int short x2, int short x3) { int short z; z = x1 + x2 + x3; return(z); } 8. C 프로그램에서 Program 에서 Assembly Language 작성된함수를 Call 하는경우의예 C 언어로작성된 main 프로그램, 이프로그램에서 Assembly 언어로작성된함수를 Call 한다. // S/W Environment : AVR Studio + WINAVR Compiler // Made by chowk. # ifndef atmega128 #define atmega128 # endif
#define FOSC 16000000// Clock Speed #include <avr/io.h> #include <stdio.h> #include <avr/interrupt.h> int short asm_add( int short, int short, int short); void init_devices(void); //Initialize all peripherals void init_devices(void) { cli(); DDRF = 0xff; // Disable all interrupts // PORTF : Output PORT PORTF = 0x00; DDRD = 0x00; PORTD = 0xff; sei(); // PORTD : Input PORT // Input PORT Pull up // Re-enable interrupts } int short sum = 0 ; // global variable, bss section에할당된다. int main(void){ init_devices(); // Assembly subroutine을 call 할때 Parameter passing 과
// call 및 ret 과정을이해하기위함. // 이프로그램실험 ( Listing File에서 Parameter passing 등을 // 이해하기위함 ) 을위하여는 Compile Option Os 를사용 // 하면안된다. ( 상수만 Pass 하고그연산결과를 Return 하는 // 함수는최적화과정에서생략되기때문 ) sum = asm_add(0x0104, 0x0205, 0x0306); for( ; ; ){ // SW의상태를읽어서, // SW0가 Open 된경우 sum의 LSD(8-bit) 를 LED에출력하고, // SW0가 Closed 된경우 sum의 MSD(8-bit) 를 LED에출력한다. if((pind & 0x01)!= 0) PORTF = (char)(sum & 0x00ff); else PORTF = (char)((sum >> 8) & 0x00ff); } return 0; } Assembly 언어로작성된 c 함수 // Project Name : c_call_asm_func_3v_add // Author: chowk // Lab: KHU // c언어로작성된프로그램에서 Assembly 언어로작성된함수를 call 할때인수의전달과연산결과를 Return하는과정을이해하기위한예제임.
# ifndef atmega128 #define atmega128 # endif #define FOSC 16000000// Clock Speed #include <avr/io.h> // Definition file for ATmega128 #include <avr/interrupt.h> // [Add all hardware information here] // Program Constants // Program Variables Definitions #define RG_TEMP r0 // Temporary Register #define RG_ZERO r1 // #define AC0 r16 // #define AC1 r17 // // SRAM Definitions // Needed to subtract 0x20 from I/O addresses #define SFR_OFFSET 0.section.data.ORG 0x0100
//cbyte:.byte 0x00 // Allocata byte(s) of storage with initialized values //cword:.word 0x0000 // Allocata word(s) of storage with initialized values //clongword:.long 0x00000000 // Allocata 32-bit long word(s) of storage with initialized values.global asm_add.section.text // Function Entry asm_add: add r22, r24 // x1의 LSD(r24) 와 x2의 LSD(r22) 를더한다. adc r23, r25 // x1의 MSD(r25), x2의 MSD(r23) 와 Carry를 더한다. add r22, r20 // ((x1의 LSD(r24) + (x2의 LSD(r22))) 에 X3 의 LSD(r20) 를더한다. adc r23, r21 // ((x1의 MSD(r25) + (x2의 MSD(r23))) 에 X3의 MSD(r21) 와 Carry를더한다. movw r24, r22 // r25:r24 를이용하여결과를 Return 하기위하여계산결과를 r25:r24에복사한다. ret.end