make와 같은 ANT라는 컴파일러가 있는것 같다..물론 좀더 봐야 뭔지 알겠지만...
트랙백으로 좀 확인해 봐야 겠다.
이 내용은 하기 내용에서 trackback 해왔다.
ANT가 Make보다 좋은 점은 각종 서적과 문서에 나와 있으므로 패스.

우선, CVS에서 프로젝트를 checkout

[root@fedora cvs]# cvs checkout hibernate-start01
cvs checkout: Updating hibernate-start01
cvs checkout: Updating hibernate-start01/WebContent
U hibernate-start01/WebContent/test.jsp
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF
U hibernate-start01/WebContent/WEB-INF/web.xml
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF/classes
U hibernate-start01/WebContent/WEB-INF/classes/hibernate.cfg.xml
cvs checkout: Updating hibernate-start01/WebContent/WEB-INF/lib
U hibernate-start01/WebContent/WEB-INF/lib/antlr-2.7.6.jar
U hibernate-start01/WebContent/WEB-INF/lib/asm-attrs.jar
U hibernate-start01/WebContent/WEB-INF/lib/asm.jar
U hibernate-start01/WebContent/WEB-INF/lib/c3p0-0.9.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/cglib-2.1.3.jar
U hibernate-start01/WebContent/WEB-INF/lib/commons-collections-3.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/commons-logging-1.0.4.jar
U hibernate-start01/WebContent/WEB-INF/lib/dom4j-1.6.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/ehcache-1.2.3.jar
U hibernate-start01/WebContent/WEB-INF/lib/hibernate3.jar
U hibernate-start01/WebContent/WEB-INF/lib/javassist-3.4.GA.jar
U hibernate-start01/WebContent/WEB-INF/lib/jta-1.1.jar
U hibernate-start01/WebContent/WEB-INF/lib/log4j-1.2.11.jar
U hibernate-start01/WebContent/WEB-INF/lib/servlet-api.jar
U hibernate-start01/WebContent/WEB-INF/lib/slf4j-api-1.5.6.jar
U hibernate-start01/WebContent/WEB-INF/lib/slf4j-jdk14-1.5.6.jar
cvs checkout: Updating hibernate-start01/resources
U hibernate-start01/resources/jdbc.properties
cvs checkout: Updating hibernate-start01/src
cvs checkout: Updating hibernate-start01/src/com
cvs checkout: Updating hibernate-start01/src/com/greatshin
cvs checkout: Updating hibernate-start01/src/com/greatshin/hibernatestart
U hibernate-start01/src/com/greatshin/hibernatestart/Event.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/Event.java
U hibernate-start01/src/com/greatshin/hibernatestart/EventManager.java
U hibernate-start01/src/com/greatshin/hibernatestart/EventServlet.java
U hibernate-start01/src/com/greatshin/hibernatestart/HibernateUtil.java
U hibernate-start01/src/com/greatshin/hibernatestart/Person.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/Person.java
U hibernate-start01/src/com/greatshin/hibernatestart/User.hbm.xml
U hibernate-start01/src/com/greatshin/hibernatestart/User.java
U hibernate-start01/src/com/greatshin/hibernatestart/UserManager.java
U hibernate-start01/src/com/greatshin/hibernatestart/UserServlet.java
[root@fedora cvs]# ls -al


방금 checkout한 hibernate-start01 프로젝트를 빌드해보자.

ANT는 빌드정보를 XML파일로 관리한다.

아래와 같이 작성하자.

build.xml의 속성 엘리먼트는 매뉴얼을 참조하자.

<?xml version="1.0"?>
<project name="spring-template-web" basedir="." default="start">
 <property file="build.properties" /> 

 <property environment="env" />
 <property name="tomcat.home" value="${env.CATALINA_HOME}" />
 <property name="deploy.dir" value="${tomcat.home}/webapps/${webapp.name}" />

 <path id="classpath">
  <fileset dir="${lib.dir}" includes="*.jar" />
  <pathelement path="${build.dir}" />
 </path>

 <target name="compile" description="Compile main source tree java files">
  <mkdir dir="${build.dir}/classes" />

  <javac destdir="${build.dir}/classes" debug="true" optimize="false" deprecation="false" failonerror="true">
   <src path="${src.dir}/com" />
   <classpath refid="classpath" />
  </javac>

  <!-- Copy XML files to ${build.dir}/classes -->
  <copy todir="${build.dir}/classes">
   <fileset dir="${src.dir}" includes="**/*.xml" />
  </copy>

  <native2ascii src="${resources.dir}" dest="${build.dir}/classes" includes="**/*.properties" />
 </target>

 <patternset id="war.files">
  <include name="**/*.*" />
  <exclude name="**/cargo*.jar" />
  <exclude name="**/junit.jar" />
  <exclude name="**/jwebunit*.jar" />
  <exclude name="**/httpunit*.jar" />
  <exclude name="**/*mock*.jar" />
  <exclude name="**/servlet-api*.jar" />
  <exclude name="**/strutstestcase*.jar" />
  <exclude name="**/Tidy.jar" />
 </patternset>

 <target name="war" depends="compile" description="Packages app as WAR">
  <mkdir dir="${dist.dir}" />
  <lib dir="${common.lib.dir}">
   <exclude name="**/servlet-api.jar" />
   <exclude name="**/junit.jar" />
  </lib>
  <war destfile="${dist.dir}/${webapp.name}.war" webxml="${web.dir}/WEB-INF/web.xml" compress="true">
   <classes dir="${build.dir}/classes" />
   <fileset dir="${web.dir}">
    <patternset refid="war.files" />
    <exclude name="**/web.xml" />
   </fileset>
  </war>
 </target>

 <target name="deploy" depends="compile" description="Deploy application">
  <copy todir="${deploy.dir}" preservelastmodified="true">
   <fileset dir="${web.dir}">
    <patternset refid="war.files" />
   </fileset>
  </copy>
  <copy todir="${deploy.dir}/WEB-INF/classes" preservelastmodified="true">
   <fileset dir="${build.dir}/classes" />
  </copy>
  <copy todir="${deploy.dir}/WEB-INF/lib" preservelastmodified="true">
   <fileset dir="${lib.dir}">
    <exclude name="**/junit.jar" />
    <exclude name="**/servlet-api.jar" />
   </fileset>
  </copy>
 </target>

 <target name="deploywar" depends="war" description="Deploy application as a WAR file">
  <copy todir="${deploy.dir}" preservelastmodified="true" file="${dist.dir}/${webapp.name}.war" />
 </target>

 <target name="clean" description="Clean output directories">
  <delete dir="build" />
  <delete dir="${dist.dir}" />
 </target>

 <!-- Tomcat Ant Tasks -->
 <taskdef file="tomcatTasks.properties">
  <classpath>
   <pathelement path="${tomcat.home}/server/lib/catalina-ant.jar" />
  </classpath>
 </taskdef>

 <target name="remove" description="Remove application from Tomcat">
  <undeploy url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="reload" description="Reload application in Tomcat">
  <reload url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="start" depends="stop" description="Start Tomcat application">
  <start url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="stop" depends="deploy" description="Stop Tomcat application">
  <stop url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" path="/${webapp.name}" />
 </target>

 <target name="list" description="List Tomcat applications">
  <list url="${tomcat.manager.url}" username="${tomcat.manager.username}" password="${tomcat.manager.password}" />
 </target>

</project>

build.xml 파일 작성이 완료되었으면 아래와 같이 빌드한다.

위 설정파일에는 컴파일 후 배포까지 포함되어 있음을 참조하자.

[root@fedora hibernate-start01]# ant
Buildfile: build.xml
compile:
    [mkdir] Created dir: /home/cvs/hibernate-start01/build/classes
    [javac] Compiling 8 source files to /home/cvs/hibernate-start01/build/classes
     [copy] Copying 3 files to /home/cvs/hibernate-start01/build/classes
[native2ascii] Converting 1 file from /home/cvs/hibernate-start01/resources to /home/cvs/hibernate-start01/build/classes
deploy:
     [copy] Copying 12 files to /usr/local/tomcat/webapps/hibernate-start01/WEB-INF/classes
stop:
     [stop] OK - Stopped application at context path /hibernate-start01
start:
    [start] OK - Started application at context path /hibernate
이 글은 하기 링크에서 참고한 것입니다.
http://scitech.tistory.com/47

[참고] 현재 제가 이용하는 설정은 다음과 같습니다. 환경에 따라 본 결과와 다를 수도 있습니다.
  1. 윈도우 XP 서비스 팩 2 (Microsoft Windows XP, Service Pack 2)
  2. 우분투 7.04 파이스티 폰 (Ubuntu 7.04 Feisty Fawn)


윈도우(Microsoft Windows)에서 다른 윈도우를 제어하려고 할 때 많이 쓰이는 프로그램으로 원격 데스크톱 연결(Remote Desktop Connection)이 있다. 내 윈도우에서 다른 사용자의 윈도우에 접속할 때 쓰인다. 일상적으로 주로 이용하는 컴퓨터가 있고, 데이터 처리나 저장을 줄곧 그곳에서만 해야 하는 경우 많이 이용된다. 이처럼 윈도우에 접근하고 제어할 수 있도록 하는 기술은 원격 데스크탑 프로토콜(Remote Desktop Protocol, RDP)에 바탕을 두는데, 클라이언트는 굳이 윈도우가 아닌 맥 OS(Mac OS)나 리눅스(Linux)이어도 된다.

사용자 삽입 이미지

윈도우 XP(Microsoft Windows XP)의 원격 데스크톱 연결(Remote Desktop Connection)



RDP 를 이용하는 것은 아니지만 이와 동일한 기능을 구현할 수 있도록 하는 기술이 몇 가지 있다. 마침 내 경우에는 같은 장소에서 두 대의 컴퓨터를 동시에 이용하고 있지만 모니터가 하나 밖에 없는 이유로 관리가 쉽지 않았다. 그래서 이와 관련한 프로그램을 이용해 보기로 했다.

먼저 윈도우에서 이용할 클라이언트 프로그램을 받아야 한다. 여기에서는 RealVNC(http://www.realvnc.com/)를 이용하기로 한다. RealVNC는 서버 버전과 클라이언트 버전이 있는데 서버 버전은 돈을 주고 구입해야 한다. 하지만 클라이언트는 무료로 배포되고 있으니 리눅스를 서버로 쓰고 윈도우를 클라이언트로 쓰는 경우에는 아무런 불편함이 없다. 아래 그림과 같이 VNC Personal Edition for Windows를 다운로드하면 된다.

사용자 삽입 이미지

RealVNC 홈페이지



아 래 그림에서 보이듯, VNC Enterprise Edition이나 Personal Edition 모두 서버로 이용하려면 30일 시험판(trial version)을 이용해야 하지만 VNC Personal Edition Viewer for Windows는 무료로 이용할 수 있음을 볼 수 있다.

사용자 삽입 이미지

RealVNC 홈페이지



다음으로 리눅스(여기에서는 우분투)에서 서버 설정을 해 주어야 한다. 우분투(Ubuntu)에서 <시스템 → 기본 설정 → 원격 데스크탑> 메뉴로 가서 원격으로 컴퓨터에 접근할 수 있도록 설정을 해 주어야 한다. 혹시 아무나 접근하는 것을 막고자 하면 보안용으로 암호(열쇠글, password)를 설정할 수도 있다. 우분투에서 이용하는 원격 데스크탑은 Vino(http://vino.klik.atekon.de/)라는 Gnome 용 VNC Server이다.

사용자 삽입 이미지

우분투(Ubuntu)에서의 원격 데스크탑 기본 설정



다 음으로 윈도우에서 조금 전에 다운로드한 RealVNC를 실행시킨다. RealVNC Viewer는 따로 설치(installation)할 필요 없이 그냥 실행시키기만 하면 된다. Server 항목에 접근하고자 하는 컴퓨터의 IP address나 도메인 네임(domain name)을 적어 주면 된다. ([참고] 그림에서는 서버의 IP address를 123.456.789.012로 했는데, 실제로 이런 IP는 존재할 수 없습니다. IP 주소 네 자리 각각에 들어갈 수 있는 최대값이 255이기 때문입니다.)

사용자 삽입 이미지

RealVNC Viewer



접속하기 전에 환경 설정을 해 두기로 했다. 기본적으로 컬러 레벨이 64색으로 되어 있기 때문에 이를 풀 컬러(full color)로 바꾸어 주었다.

사용자 삽입 이미지

RealVNC 환경 설정



그리고 암호를 입력한다.

사용자 삽입 이미지

RealVNC - 암호 입력



이렇게 해도 윈도우에서는 다음 그림과 같이 까만 화면만 보이고 상대 컴퓨터 화면을 볼 수는 없다. 이는 우분투/리눅스에서 아직 접근 허용을 내리지 않았기 때문이다. 우분투로 돌아가면 다음과 같은 메시지가 뜬 것을 볼 수 있다.

사용자 삽입 이미지


이처럼 접근 허용을 해 주어야 윈도우에서 우분투 화면을 볼 수 있다. 다음은 이러한 과정을 통해 윈도우에서 우분투 화면을 제어하는 모습이다.

[추가] hyunin님 께서 지적하신 바에 의하면 위 <우분투>에서 <원격 데스크탑 환경 설정>을 할 때, <확인을 하도록 물어보기> 부분을 해제하면 이러한 과정을 거치지 않고 바로 접속할 수 있다고 합니다. 감사드립니다.


사용자 삽입 이미지

RealVNC에서 우분투에 접근한 모습



이 처럼 윈도우와 우분투 시스템이 물리적으로 가까운 거리에 있을 때에는 윈도우에서 RealVNC 등의 VNC 클라이언트를 이용하여 우분투/리눅스 컴퓨터를 제어할 수 있다. 이렇게 하면 우분투/리눅스 화면을 고스란히 윈도우에서 볼 수 있기 때문에 직접 컴퓨터를 제어하는 것과 동일한 효과를 볼 수 있다. 하지만 한 가지 단점이라면, 윈도우에서 우분투/리눅스에 접근하고자 할 때, 우분투/리눅스에서 접속 승인을 해야 하는 점이라 하겠다.
하기의 글은 아래 링크의 글을 퍼온 것이니 참고 하세요
http://bluluv.net/?document_srl=2598&mid=fdp_lin&listStyle=&cpage=


외근을 나갔거나 출장중일때 혹은 여행을 갔을때, 사무실이나 집안의 컴퓨터를 원격으로 제어할 필요가 있습니다.
우분투에서 원격지에 있는 컴퓨터에 접속 하는 손쉬운 방법을 알아 보겠습니다.

원격제어를 당할 (사무실이나 집안의 컴퓨터를 말합니다) 컴퓨터의 세팅부터 해보겠습니다.
'시스템 -> 기본설정 -> 원격 데스크탑'을 선택 합니다.

2.jpg

일반과 고급 두가지 탭이 나타나는데 하나씩 알아보도록 하겠습니다.

* 일반
'다른 사용자가 데스크탑을 볼 수 있도록 합니다'에 체크를 하게 됨으로서 원격제어를 할 수 있는 준비가 시작됩니다.
체크를 하게 되면 아래에 비활성화 되어 있던 메뉴들이 나타나게 되는데 하나하나 짚어 보며 알아 보겠습니다.

1.jpg

  • 다른 사용자가 데스크탑을 볼 수 있도록 합니다 : 원격지 컴퓨터에서 현재의 컴퓨터를 볼 수만 있습니다. 다른 작동이나 조작은 할 수 없습니다.
  • 다른 사용자가 데스크탑을 제어할 수 있도록 합니다 : 원격지 컴퓨터에서 현재의 컴퓨터를 볼 수 있음은 물론 모든 조작을 할 수 있습니다. 심지어 컴퓨터를 끄는일까지 가능합니다.
  • 확인을 하도록 물어보기 : 원격지에서 현재의 컴퓨터에 접속 할 때 현재의 컴퓨터에서 누군가가 허가 버튼을 눌러줘야만 제어가 가능합니다. 만일 현재의 컴퓨터에 아무도 없다면 허가 버튼을 누를 수 없게 되어 원격지에서 접속은 되지만 아무런 행동도 못하게 됩니다.
  • 사용자가 이 암호를 입력하여야 합니다 : 원격지에서 접속 시 설정해놓은 암호를 입력 해야만 접속이 가능합니다. 보안상 암호를 설정하는것이 좋습니다.
* 고급

3.jpg
  • 로컬 연결만 허용 : 내부 내트워크에서만 연결을 허용 합니다.
  • 보조 포트 사용 : 기본 포트 5900번 외에 다른 포트를 사용합니다.
  • 암호화 필요 : 원격지에서 접속시 비밀번호를 입력하도록 설정해 놓으면 그 비밀번호를 암호화 해서 전송합니다. 보안상 체크 해두는것이 좋습니다.
  • 연결이 끊기면 화면 잠그기 : 원격지에서 접속하여 작업을 할 경우 모든 작업 화면이 현재 컴퓨터에 보이기 때문에 접속을 끊게 될 경우 화면을 잠궈두는 역할을 합니다. 보안상 체크해 두는것이 좋습니다.
이제 원격으로 연결을 하는 방법을 알아 보겠습니다.
준비 작업으로 접속할 컴퓨터의 IP를 알고 있어야만 접속이 가능합니다.

'프로그램 -> 인터넷 -> 원격 데스크탑 보기'를 실행 합니다.

4.jpg

'연결'버튼을 누릅니다.

'호스트'에 접속할 컴퓨터의 IP주소를 넣습니다.

6.jpg

설정해준 비밀번호를 입력합니다.

5.jpg

접속이 된것을 보실 수 있습니다.

7.jpg

몇가지 메뉴가 있으나 어렵지 않게 사용하실 수 있을거라 생각 됩니다.
그중 편리하게 사용 할 수 있는 기능중에 책갈피 기능이 있는데 일종의 즐겨찾기 기능과 같다고 생각하시면 됩니다.

우분투에서는 이렇게 원격지의 컴퓨터에 접속하여 작업을 수행 할 수 있는 기능을 제공 합니다. 또한 원격지의 컴퓨터가 리눅스가 아니라 윈도우일 경우에도 접속이 가능합니다.

'프로그램 -> 인터넷 -> 터미널 서버 클라이언트'를 실행해 봅니다.

8.jpg

사용법은 윈도우의 그것과 똑같습니다. 자세한 사용법은 여기서 다루지 않겠습니다.

윈도우에 접속한 모습입니다.

9.jpg

이처럼 우분투에서는 강력한 원격 제어를 제공합니다.
이제 멀리 떨어져 있어도 걱정없이 모든 작업을 수행할 수 있습니다.
이상 우분투에서의 원격 제어에 대해서 알아 보았습니다.

ARM7 강좌 [15] : 최종회

 


  드디어 ARM7강좌 최종회를 쓰게 되었습니다. 제가 ARM7을 기준으로 강의를 해 왔지만, 실제로 사용했던 CPU는 StrongARM이었습니다. 그래서, 오늘은 StrongARM SA1100에 대해서 간단히 말해 보고자 합니다.(사실 별로 아는 것도 없지만..)


  물론 자세한 Spec은 메뉴얼을 통해 살피시는 것이 더 정확할 테니까 제가 말씀드려봐야 별 의미가 없을 듯 하고.


  그냥 StrongARM을 사용하면서 인상깊었던 점을 몇 가지 말씀드리고자 합니다.


  첫 번째로 가장 중요한 점으로 생각되는 것은 CPU 동작속도가 200MHz정도까지를 지원한다는 점입니다. 이전에 접했던 Embedded CPU 가 빨라야 50MHz이었던 것을 감안한다면, 참 빠른 CPU입니다. 그런데 이 칩은 신기하게도 열이 별로 안난다는 것이 또한 인상적입니다.


  보드를 몇 일씩 켜 놓아도 안정적이고, 불안하지가 않더군요. 옛날에 AMD188을 사용할 때에는 잠시만 켜 놓아도 보드가 녹아나는 것이 아닐지 얼마나 불안했던지 그만큼 전력 소모도 작다는 뜻이겠지요.


  두 번째로는 LCD 컨트롤러가 내장되어 있어서 1024*1024의 해상도까지 지원하고, 컬러는 3만 컬러까지 지원합니다. 역시 일반 On-Board칩과 비교할 만한 일은 못되겠지만, 그런 칩만 다루었던 제게는 참 흐뭇한 일이었습니다. SA1100 보드에 640*480 TFT LCD가 달려있었는데, 거기에 고소영 사진을 띄워놓고 얼마나 흐뭇해했던지...^^


  세 번째 인상 깊었던 것은 MMU 입니다. 학교 다닐 때 OS 수업시간에나 들어봤던 MMU라는 것을, 직접 프로그램 해야 했으니까요. 나중에 차차 알게된 사실이지만, 이 MMU가 있는지의 여부가 Linux와 같은 OS를 포팅 할 경우에는 무척 중요하다고 하군요. StrongARM의 경우는 Two-Level Virtual Memory를 지원합니다. 메모리를 최소 400H Byte단위로, 최대 1Mega Byte단위로 제어할 수 있습니다.


  이밖에도 GPIO에서 Edge Detect Interrupt기능을 지원한다는 사실과, PCMCIA와 같은 각종 주변장치를 지원한다는 것 등.


  StrongARM은 Embedded CPU 보다는 PC용 CPU에 근접 하다는 느낌이 들었고. 그럼에도 불구하고 낮은 전력소모 때문인지 Embedded용으로 많이 사용되고 있는 것 같습니다.


  ARM7강좌를 하면서 개발환경에 대해서 궁금해하시는 분들이 계셨습니다.

  “ARM7을 공부해 보고 싶은데 보드가 없다“... 라든가 하는 식의.

  그렇게 보면 제가 운이 좋았던 것 같습니다. 회사에 정말 훌륭한 보드가 있었거든요.


  물론 제품을 위해 사용되는 보드였기는 하지만... LCD와 Keyboard,  카메라

까지 달린 보드였고, 충분한 메모리와 Flash까지 있었으니까요...


  사실 여전히 ARM을 공부하기 위한 보드를 찾기는 쉬운 일이 아닌 것 같더군요. 저도 언젠가는 StrongARM보드를 만들어 보리라 생각했었지만. 그게 그리 만만치 않은 일이었고. 앞으로도 가능할지는 모르겠습니다.


  별로 할 말도 없으면서 강좌를 쓰려니 이상한 말만 나오는군요...

  

  모두에게 감사한 마음과 죄송한 마음을 갖고 있습니다.


  사실 시작할 때의 마음가짐을 끝가지 가지고 가지 못했던 것도.. 그렇고..


  이렇게 강좌란에 허튼 소리를 쓰고 있는 것도 그렇고..


  강좌를 해 오면서, 정말 많이 알고, 잘 하시는 분들이 참 많구나 하는 생각을 했습니다. 거기에 비하면 이제 막 공부를 하고 있는 제가 이런 강좌를 주제넘게 시작했구나 하는 후회도 들었고요. 부끄러운 생각도 많이 들었습니다.


 그동안 관심을 가져주신 모든 분들께 다시 한번 감사 드리면서 ARM7 강좌를 이렇게 정리할까 합니다.

ARM7 강좌 [14] : Instruction Set(8)

 


  오늘 다룰 명령은 SWP와 SWI 두 가지입니다. Co-Process 명령을  제외하면 이 명령이 마지막이겠군요. 코프로세서는 다루지 않을 생각이니. 참 홀가분한 느낌입니다.


1. Single Data Swap : <SWP>{cond}{B}    Rd,Rm,[Rn]


   레지스터를 3개 지정하도록 되어 있습니다. 실제 동작은 Rd:=[Rn], [Rn]:=Rm이 한번에 일어나는 명령입니다. 여기서 한번이라는 말은 한 Clock을 의미하는 것이 아니라, 명령 도중에 인터럽트 등에 의해 중단되지 않고 계속 이루어진다는 것입니다. 그리고 B접미사는 계속 보아 왔듯이 워드 Operation이 아니라 Byte Operation을 의미합니다.


   예제를 보죠.


        SWP     r0,r1,[r2]

                ; r0:=[r2], [r2]:=r1 입니다.


        SWPB    r2,r3,[r4]

                ; r2:=[r4], [r4]:=r3 입니다. 다만 B가 붙었으므로 Bit 0에서 7까지만 영향을 미칩니다.


        SWPEQ   r0,r0,[r1]

                ; Flag의 상태를 확인하여 r0와 [r1]의 내용을 바꾸는 형태입니다. 보셨듯이 Rd와 Rm이 같을 수도 있군요.


   한가지 더 언급하고 싶은 것은 SWP를 세마포어 구현에 사용하는 방법입니다. 어떤 문서에선가 SWP명령이 세마포어를 구현하는데 유용하다고 읽었는데, 처음에는 이해가 안되더군요. 한참 뒤에 이해할 수 있었습니다.


   인터럽트를 금지시키지 않고 Semaphore 연산을 할 수 있다는 것이 SWP명령의 강점입니다. 여기서 Semaphore를 설명 드리기는 좀 그러니, 간단히 핵심만 말씀드리고, 이해하실 분은 하시고, 안해도 별 수 없겠죠.


   보통 세마포어의 p()연산을 구현하는 순서로, 먼저 세마포어 변수를 읽어오고, 다음에 비교를 하죠. 그 값이 0보다 크다면 감소를 시켜  다시 Write합니다. Test and Set 연산이라고도 하던데요.


   SWP명령에 적용시키려면, [Rn]부분이 변수가 되어야겠군요. 그러면 Rd에는 SWP명령을 통해 세마포어 변수 값이 들어오고요, 물론 세마포어 변수의 상태를 알지 못하므로 어떤 값이 들어올지는 모릅니다. 따라서 이 단계에서는 세마포어 변수에 어떤 값을 다시 Write해야 할지도 모릅니다.


   핵심은 변수에 Write되는 값을 무조건 0으로 설정한다는 것이죠. 즉.


   r3가 Semaphore 변수의 주소를 가리키고 있다고 하고, r2의 값은 0이라고 한다면,


        SWP     r1,r2,[r3]


   와 같은 명령으로 처리할 수 있습니다. 위의 명령을 통해 원래 세마포어 변수의 값이 r1으로 로드됩니다. 0보다 큰 지의 여부는 이제부터 비교해야겠지요. 한가지 특이한 것은 세마포어 값을 읽어옴과 동시에 0을 넣어주었다는 것입니다. SWP명령의 특성상 읽어오는 동작과 0을 넣는 동작 사이에는 인터럽트 등이 걸릴 수 없습니다.


   이제, r1의 세마포어 값을 비교하는 부분으로 넘어가면 되는데, 이 와중에 인터럽트가 걸리고 다른 Task가 세마포어 변수를 Access하려고 하더라도 0이 들어 있으니 자연히 대기상태로 인식되어 더 이상 진행을 하지 못하게 될 것입니다.


   이밖에도 v()연산에서도 뭔가 더 해주어야 하겠지만.

   (괜한 얘기를 꺼냈나 하는 후회가 엄습하는 중입니다...^^)



2. Software Interrupt : SWI{cond}  <expression>


   SWI명령은 소프트웨어 인터럽트 명령입니다. 8086에는 INT 명령이 있죠? Exception 파트를 보시면 언급이 있었습니다.


   SWI명령이 걸리면 동작모드가 변화합니다. Supervisor 상태로 진입을 하게 됩니다. 이런 특성 때문에 pSOS의 경우는 System Call의 진입방법으로 사용합니다.


   명령의 형식을 보시면 <expression>부분이 있는데, 무슨 역할을 하는 것일까요? 8086계열처럼 INT 21H 또는 INT 10H 이런 식으로 인수를 주는 것일까요? 8086이야 Interrupt 벡터를 지정하는 것이었지만, ARM7에는 해당 Vector는 고정이 되어 있습니다.(Software Exception 한가지로)


   CPU는 expression에 대한 아무런 일도 하지 않습니다. 실제 명령어 구조를 보면 총 32비트 중 상위 4비트는 조건 Flag에 사용되고, 다음 4비트가 모두 1111 이면 SWI명령으로 판단됩니다. 이후 24비트는 실제로는 무시되는 내용입니다만...


   위와 같이 써주면 expression의 내용이 SWI명령의 Low 24Bit부분에 인코딩 됩니다. 하지만 말씀 드렸듯이 ARM7은 SWI명령의 Low 25Bit내용이 뭐든 간에 동일하게 동작합니다.


   그럼 왜 그렇게 쓰는걸까요?


   예제를 보면 SWI명령을 만나면 소프트웨어 Exception Handler로 이동하고 복귀번지는 r14_svc로 저장이 됩니다. 그런데 Handler 부분에서 복귀 번지를 이용하여 해당 명령을 직접 읽어다가 Low 24 Bit 부분을 인수로 해석해서 사용할 수 있는 것입니다.


   물론 이런 일들은 ARM7이 해주는 것이 아니라 User가 Handler에서 직접 프로그램 하는 것이죠.


   다음은 Handler 부분의 예제입니다.


        STMFD   r13,{r0-r2,r14}

        LDR     r0,[r14,#-4]

        BIC     r0,r0,#0xFF000000

        ...


   위에서 보시면 결국 r0에 SWI명령의 low 24Bit가 얻어지죠.


  오늘 강좌까지 길고 길었던 정말 길었던 ARM Instruction 부분을 끝냈습니다.


  계획했던 분량은 3회였는데... 결국 8회 분량으로 늘어나고 말았군요. 이것저것 후회가 많았던 부분이었습니다. 이렇게 저렇게 숙제하듯이 여기까지 해치우고는...


  다음엔 무엇을 써야 할지... 고민을 하게 되는군요.


  뭐가 되었든 다음 강좌로 ARM7 강좌를 정리하겠습니다. (마음의 준비를...^^)

  그럼 이만.


ARM7 강좌 [13] : Instruction Set (7)

 


● Block Data Transfer 명령(LDM,STM)


  해당 명령은 개인적인 생각으로 참 독특하다고 생각합니다. 지난 강좌에서 다루었던 LDR, STR과 마찬가지로 실제 메모리에 레지스터의 내용을 전달하거나, 전달받을 수 있는 명령입니다. ARM7에서는 이런 명령이 몇 안되죠...


  LDR명령이 메모리 번지의 내용을 지정된 레지스터로 가져오는 명령이라면 LDM은 가져오긴 하는데, 여러 개의 레지스터의 내용을 한 큐에 가져오는 명령입니다. 가장 많이 사용되는 경우는 스택 연산인 것 같습니다. 전에 말씀드렸었지만, ARM7에는 Push, Pop 명령이 없습니다. 대신 LDR이나 STR을 쓸 수도 있겠고, 또 LDM이나 STM을 쓸 수도 있죠. 후자 쪽이 더 많이 사용되는 듯 합니다.


  1) <LDM|STM>{cond}mode   Rn{!},{reg_list}{^}


    위 명령에서 {cond}는 늘 보아오던 명령어 실행 조건입니다. Rn은 전송에 사용될 베이스 번지를 지정하는 레지스터입니다. !를 붙이면 Wrte Back 기능이죠. LDR과 STR에서 다루었습니다. 자세한 내용은 뒤에서 언급하도록 하겠습니다.


    {reg_list} 부분은 전송하거나 전송 받을 레지스터의 목록을 나타내는 부분입니다. 예를 들어 1000번지에 r1,r2,r3를 저장하고 싶다 했을 경우에, 일단 1000번지 값을 어떤 레지스터에 넣어두고, 여기서는 그 레지스터를 r13이라고 하죠, 그러면 Rn은 r13이 되는 거구요, {reg_list}는 {r1,r2,r3} 이 되는 것입니다. mode 라고 되어있는 부분은 여러 개의 레지스터를 메모리에 넣거나 가져올 때 어떤 방식으로 동작할 지를 지정하는 접미사입니다. 이 접미사 종류가 8가지가 있는데요.


    무지하게 복잡해 보입니다. 일단은 각 요소의 의미만 간단하게 정리하고, 다음으로 넘어가죠.


    마지막으로 {^}부분은... 글쎄요 잘 이해가 안되는 부분입니다만, 제가 보는 책과 데이터 시트에서 예제가 나와 있지 않군요. 그냥 문서의 내용을 그대로 적어보겠습니다.


    {^} if present set S bit to load the CPSR along with the PC, or

        force transfer of user bank when in privileged mode...(???)


    자, 그럼 이제 본격적으로 설명에 들어가겠습니다.


    먼저 1단계, LDM 과 STM의 의미입니다.


       LDM : 베이스 레지스터(Rn)로 지정된 번지에서 레지스터 목록으로 지정된 각 레지스터의 내용을 읽어들이는 명령.

       STM : LDM과 반대.


    여기까지는 별 무리가 없으리라 생각합니다. 혹시 이 명령을 8086등에 있는 Block Data 전송명령 등과 혼동하지 않으시길 바랍니다. 어렴풋이 기억하는데, 8086등에는 메모리에서 메모리로 Block 전송을 할 수 있는 명령이 있죠... 또는 특정 길이만큼 메모리를 어떤 값으로 설정하는 Block 설정 명령도 있었던 듯 합니다.


    ARM7의 LDM과 STM은 Block 전송이 아니라 Multiple Register 전송입니다.

    쉽게 생각하면


        push    ax

        push    bx

        push    cx

        push    dx


    가 8086형태라고 할 때, ARM7에서 Single 데이터 전송 명령을 사용하면


        STR     r0,[sp],#4

        STR     r1,[sp],#4

        STR     r3,[sp],#4

        STR     r4,[sp],#4


     가 되겠고, 또 Multiple 전송명령을 사용하면,


        STMEA   sp!,{r0,r1,r2,r4}


     제가 방금 적어본 것이라 맞는 것인지 확신은 없습니다만, 그냥 개념이 이렇다는 것만 파악하셨으면 합니다.


     그러면 이제 2단계, {Reg_List}를 자세히 다루어 보죠. 위의 예에서 나와있듯이 중괄호 사이에 전송 대상이 되는 레지스터를 넣어주면 됩니다. 그렇다면 몇 개까지 가능한 것일까요? ARM7에서 한 시점에 사용할 수 있는 레지스터의 개수는 r0에서 r15까지 총 16개죠.  LDM이나 STM명령에서 지정할 수 있는 레지스터의 개수는 최대 16개입니다. 즉, 한 명령으로 모든 레지스터를 저장하거나, 가져올 수 있다는 의미입니다.


     참 재미있는 사실은, LDM명령의 니모닉상에 16비트의 공간이 있어서 각 비트가 레지스터 r0-r15와 1:1로 대응이 된다는 사실입니다. 따라서 {reg_list}에는 어떠한 레지스터의 조합도 올 수 있습니다. 감동 !


     그리고 어셈블러의 문제겠지만, {r0,r2} 이런 형식뿐만 아니라, {r0-r5} 와 같은 형식, {r0-r3,r6-r7} 이런 형식도 사용할 수 있습니다.


     마지막으로 확인할 것은, {r3,r2,r1} 이렇게 썼을 때와 {r1,r2,r3} 이렇게 썼을 경우, 메모리에 저장되는 순서가 다를까요. 아닐까요. 말씀드렸듯이 16비트의 비트필드가 존재해서 각각 레지스터와 1:1대응이 된다고 하였으니, 소스코드에서 어떤 형식을 쓰든지 간에 니모닉으로 변환될 때는 그 순서는 아무 의미가 없겠지요. 결국 같다는 말입니다.


     자. 이번에는 3단계입니다. 동작 모드!!!


     위의 STM명령에서 제가 STMEA라고 명령을 적었습니다. EA가 동작모드를 지정하는 부분입니다. 이와 같은 키워드가 8가지가 있고, 동작모드는 4가지가 있습니다.


     먼저 4가지의 동작모드를 말씀드리겠습니다.


     A) Post-Increment Addressing


        여기서 동작모드는 여러 개의 레지스터 값을 메모리로(혹은 로부터) 전송할 경우 해당 메모리 번지를 증가시키면서 저장할지, 혹은 감소시키면서 저장할지를 지정하는 것과, 증/감을 하는데, 저장하기 전에 증/감을 할지, 아니면 저장하고 나서 증/감을 할지를 지정하는 것을 의미합니다.


        처음 설명할 동작모드는 저장 이후 증가하는 방식입니다.


        예를 들어 R10에 0x1000이 들어있다고 가정하고 R10을 베이스레지스터로 사용해서 {r1,r2}를 저장한다면, Post-Increment모드에서는,


                1. 0x1000 번지에 r1이 저장된다.

                2. Base 번지값이 0x1004로 증가한다.

                3. 0x1004 번지에 r2가 저장된다.

                4. Base 번지값은 0x1008로 증가한다.


        만약 !를 사용했다면 r10의 값은 0x1008이 될 것입니다.


    B) Pre-Increment Addressing


        말 그대로 먼저 증가하고 다음에 저장하는 동작 모드입니다. 위와 같은 조건을 가정해 봅시다.


                1. Base 번지값이 0x1004로 증가한다.

                2. 0x1004번지에 r1이 저장된다.

                3. Base 번지값이 0x1008로 증가한다.

                4. 0x1008번지에 r2가 저장된다.


        역시 !를 사용했다면 r10의 값은 0x1008이 됩니다.


    C) Post-Decrement Addressing


        이번에는 베이스 번지가 감소하는 경우죠. 좀 특이한 것은, 감소 모드로 저장(로드)을 할 경우 아까는 레지스터번호가 빠른 것부터 저장하거나 불러들였는데, 이번에는 거꾸로 라는 것입니다. 결과적으로, 메모리에 저장되는 레지스터의 순서는 항상 동일하다는 것이죠. 마찬가지로 같은 조건에서 예제를 들겠습니다.


                1. 0x1000번지에 r2가 저장된다.(!!! r1이 아니라 r2)

                2. Base번지값이 0x0FFC로 감소된다.

                3. 0x0FFC번지에 r1이 저장된다.

                4. Base번지값이 0x0FF8로 감소된다.


        만약 !를 사용했다면 r10의 값은 0xFF8이 된다.


    D) Pre-Decrement Addressing


                1. Base번지값이 0x0FFC로 감소된다.

                2. 0x0FFC번지에 r2가 저장된다.

                3. Base번지값이 0x0FF8로 감소된다.

                4. 0x0FF8번지에 r1이 저장된다.


        만약 !를 사용했다면 r10의 값은 0xFF8이 된다.


    여기까지 4개의 동작모드를 설명했습니다.


    동작모드를 나타내는 키워드는 8개인데요. 각각의 동작모드에 대해서 스택처럼 사용할 경우, 혹은 아닐 경우 2가지로 나누어서 나타내기 때문입니다.


    다음 표는 각 동작모드에 대한 명령어입니다.


    ==================================================================

           동작                         Stack           Other

    ------------------------------------------------------------------

        pre  increment load             LDMED           LDMIB

        post increment load             LDMFD           LDMIA

        pre  decrement load             LDMEA           LDMDB

        post decrement load             LDMFA           LDMDA


        pre  increment store            STMFA           STMIB

        post increment store            STMEA           STMIA

        pre  decrement store            STMFD           STMDB

        post decrement store            STMED           STMDA

    ==================================================================


    Stack인 경우와 아닌 경우 키워드가 다른 것은, 단지 User의 편의를 위한 배려라고 생각됩니다. 즉, 키워드를 LDMED로 썼을 경우나 LDMIB로 썼을 경우, 동작상에 차이는 없는 듯 합니다.


    키워드를 무작정 붙인 것 같지는 않고요. 먼저 Other의 키워드를 살피면, I는 increment를 의미하고요.  D는  decrement겠죠. 그리고 B는 Before를, A는 After를 의미합니다.


    그러므로 만약 LDMDA 는 post decrement 모드를 의미하는 것입니다.


    Stack의 경우엔, E는 Empty를 F는 Full을 의미한답니다. 스택을 구현하는 경우 현재 sp가 가리키는 번지의 내용이 차있는지, 비어있는지를 의미한다고 보시면 될 듯 합니다. 무슨 이야기인가 하면 만약 Post 모드를 사용한다면, 스택을 구현할 경우, 어떤 내용을 넣고 다음에 번지를 증/감하므로 결국 어떤 시점에서 스택포인터가 가리키는 번지는 비어있게 됩니다. 이 경우 Empty가 되겠죠.


    Load의 경우엔 Empty 형태의 스택이라면 sp가 가리키는 공간에 아무 내용도 없으므로 먼저 sp를 변화시키고 데이터를 가져와야겠죠. 그래서 Load는 Pre가 Empty 와 대응이 됩니다. 하지만 반대로 Store의 경우엔 Empty 형태의 스택을 위해서는 먼저 데이터를 넣고 sp를 변화시켜야 합니다. 그렇다면 post모드가 Empty와 대응이 되겠군요!


    Full은 더 이상 말 안해도 되리라 믿습니다.


    다음으로 D는 Decending, A는 Ascending을 의미합니다. 이것은 스택이 거꾸로 커지는지 아니면 반대인지와 관련이 있습니다. 8086에서는 Push를 하면 sp값이 작아지죠? 그렇다면 decending Stack이라고 볼 수 있습니다. push와 STM이 대응되므로 STM의 Decrement 모드는 D 라는 키워드를 사용했군요. Pop의 경우는 LDM과 대응되고  decending stack에서 Pop을 할 경우 sp값은 증가되어야겠죠. 그래서 LDM에서는  increment모드가 D입니다.


    A는 반대이겠죠. 결국 스택관련 명령에서는 LDM이나 STM에서 같은 접미사를 사용하면 되는 것입니다. 물론 동작 방식은 LDM과 STM에서 각기 다르지만요.


 자.. 이제 예제를 하나 보이고 오늘 강좌를 정리하려 합니다.


     STMED      sp!,{r0-r3,r14}


     BL         somewhere


     LDMED      sp!,{r0-r3,r15}


     첫 번째 명령에 STMED는 Empty Decending Stack Operation이므로 실제로는 post-decrement 동작모드를 의미하죠.. sp(r13)가 가리키는 번지에 r0,r1,r3,r14를 저장합니다. BL로 이 루틴에 들어왔다면 r14에 복귀 번지가 들어가 있다는 것을 감안하십시요.


     BL에서 뭔가 처리를 하고, 마지막으로 LDMED명령에서 레지스터를 복구합니다. 이번에는 pre-Increment 동작모드입니다.


     한가지 주의하실 점은 r14대신에 r15로 복구를 시켰다는 것입니다. ARM7에서는 ret명령 대신 mov r15,r14를 사용한다고 말씀드렸죠.


     LDMED 명령에서 레지스터 복구와 서브루틴 복귀를 동시에 처리하는 부분입니다.


  자. 오늘 강좌는 이렇게 정리할까 합니다. 거의 1달만에 강좌를 썼군요. 그동안 메일이나 게시판을 통해서 질문과 격려를 보내주신 분들에게 죄송하다는 말씀을 드려야겠군요.


이제 강좌도 종반을 향해 달리고 있습니다. 아마도 2회 정도면 계획했던 모두가 끝이 날것 같군요. 다음 강좌를 기약하며 이만 줄이겠습니다.

+ Recent posts