개요
이 글에서는 맥OS (macOS)에서 DOSBox Debugger 버전을 빌드하기 위한 방법을 기술한다. 디버깅 기능이 포함되지 않은 DOSBox는 바이너리 파일을 다운로드하여 사용할 수 있으며 빌드 또한 큰 문제가 발생되지 않기 때문에 여기에서는 언급하지 않는다.
준비
빌드는 macOS 10.15 Catalina 환경에서 dosbox-0.74-3 버전을 대상으로 수행하였다. 그러나 다른 버전에서도 크게 다르지 않을거라 생각된다.
DOSBox 소스코드 빌드를 위하여 소스코드 패키지를 다운로드 한다.
빌드를 위한 환경 구축을 위하여 brew install [패키지명]을 실행하여 아래의 패키지를 설치한다.
- automake
- sdl
기본적인 개발도구 (XCode)가 설치되어 있는 시스템에서 추가로 필요한 개발 패키지를 설치하였으며 각자의 환경에 따라 필요한 패키지를 추가 설치할 수 있다.
빌드
DOSBox 소스코드가 다운로드된 경로에서 autogen.sh 명령을 실행한다.
dosbox/dosbox-0.74-3 » ./autogen.sh
Generating build information using aclocal, autoheader, automake and autoconf
This may take a while ...
configure.ac:12: warning: The macro `AC_CONFIG_HEADER' is obsolete.
configure.ac:12: You should run autoupdate.
./lib/autoconf/status.m4:719: AC_CONFIG_HEADER is expanded from...
configure.ac:12: the top level
configure.ac:31: warning: The macro `AC_TRY_RUN' is obsolete.
configure.ac:31: You should run autoupdate.
...
Now you are ready to run ./configure.
You can also run ./configure --help for extra features to enable/disable.
“–enable-debug” 옵션을 포함하여 “./configure”를 실행한다.
dosbox/dosbox-0.74-3 » ./configure --enable-debug
checking build system type... x86_64-apple-darwin19.6.0
checking host system type... x86_64-apple-darwin19.6.0
checking for a BSD-compatible install... /usr/local/bin/ginstall -c
checking whether build environment is sane... yes
checking for a race-free mkdir -p... /usr/local/bin/gmkdir -p
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
...
config.status: creating include/Makefile
config.status: creating docs/Makefile
config.status: creating config.h
config.status: executing depfiles commands
macOS 환경에서 디버거 기능의 옵션을 포함하여 DOSBox 빌드를 수행하게 되면 아래와 같은 오류가 발생된다.
dosbox/dosbox-0.74-3 » make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
Making all in src
Making all in cpu
Making all in core_full
make[4]: Nothing to be done for `all'.
Making all in core_normal
make[4]: Nothing to be done for `all'.
Making all in core_dyn_x86
make[4]: Nothing to be done for `all'.
Making all in core_dynrec
make[4]: Nothing to be done for `all'.
g++ -DHAVE_CONFIG_H -I. -I../.. -I../../include -I/usr/local/include/SDL -D_GNU_SOURCE=1 -D_THREAD_SAFE -g -O2 -mno-ms-bitfields -MT callback.o -MD -MP -MF .deps/callback.Tpo -c -o callback.o callback.cpp
callback.cpp:435:43: warning: format specifies type 'int' but the argument has type 'Bitu' (aka 'unsigned long')
[-Wformat]
E_Exit("CALLBACK:Setup:Illegal type %d",type);
~~ ^~~~
%lu
...
debug_gui.cpp:149:22: error: member access into incomplete type 'WINDOW' (aka '_win_st')
mvaddstr(dbg.win_reg->_begy-1,0, "---(Register Overview )---");
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curses.h:322:16: note:
forward declaration of '_win_st'
typedef struct _win_st WINDOW;
^
debug_gui.cpp:151:23: error: member access into incomplete type 'WINDOW' (aka '_win_st')
mvaddstr(dbg.win_data->_begy-1,0,"---(Data Overview Scroll: page up/down)---");
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curses.h:322:16: note:
forward declaration of '_win_st'
typedef struct _win_st WINDOW;
^
debug_gui.cpp:153:23: error: member access into incomplete type 'WINDOW' (aka '_win_st')
mvaddstr(dbg.win_code->_begy-1,0,"---(Code Overview Scroll: up/down )---");
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curses.h:322:16: note:
forward declaration of '_win_st'
typedef struct _win_st WINDOW;
^
debug_gui.cpp:155:22: error: member access into incomplete type 'WINDOW' (aka '_win_st')
mvaddstr(dbg.win_var->_begy-1,0, "---(Variable Overview )---");
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curses.h:322:16: note:
forward declaration of '_win_st'
typedef struct _win_st WINDOW;
^
debug_gui.cpp:157:22: error: member access into incomplete type 'WINDOW' (aka '_win_st')
mvaddstr(dbg.win_out->_begy-1,0, "---(OutPut/Input Scroll: home/end )---");
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curses.h:322:16: note:
forward declaration of '_win_st'
typedef struct _win_st WINDOW;
^
1 warning and 5 errors generated.
make[3]: *** [debug_gui.o] Error 1
make[2]: *** [all-recursive] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2
_win_st 정의와 관련하여 다수의 오류가 발생되는데 _win_st는 ncurses에 포함된 헤더 파일에 아래와 같이 정의된다.
#if !NCURSES_OPAQUE
struct ldat;
struct _win_st
{
NCURSES_SIZE_T _cury, _curx; /* current cursor position */
/* window location and size */
NCURSES_SIZE_T _maxy, _maxx; /* maximums of x and y, NOT window size */
NCURSES_SIZE_T _begy, _begx; /* screen coords of upper-left-hand corner */
short _flags; /* window state flags */
/* attribute tracking */
attr_t _attrs; /* current attribute for non-space character */
chtype _bkgd; /* current background char/attribute pair */
_win_st 구조체 정의를 포함하기 위하여 ./configure 옵션을 아래와 같이 설정 후 빌드를 수행한다. (빌드를 이미 수행한 경우에는 “make clean” 명령을 사용한 후 과정을 수행하도록 한다)
dosbox/dosbox-0.74-3 » ./configure --enable-debug CPPFLAGS=-DNCURSES_OPAQUE=0
make 명령을 실행하면 아래와 같이 정상적으로 빌드된다.
dosbox/dosbox-0.74-3 » make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
Making all in src
Making all in cpu
Making all in core_full
make[4]: Nothing to be done for `all'.
Making all in core_normal
make[4]: Nothing to be done for `all'.
Making all in core_dyn_x86
make[4]: Nothing to be done for `all'.
Making all in core_dynrec
make[4]: Nothing to be done for `all'.
g++ -DHAVE_CONFIG_H -I. -I../.. -I../../include -DNCURSES_OPAQUE=0 -I/usr/local/include/SDL -D_GNU_SOURCE=1 -D_THREAD_SAFE -g -O2 -mno-ms-bitfields -MT callback.o -MD -MP -MF .deps/callback.Tpo -c -o callback.o callback.cpp
callback.cpp:435:43: warning: format specifies type 'int' but the argument has type 'Bitu' (aka 'unsigned long')
[-Wformat]
E_Exit("CALLBACK:Setup:Illegal type %d",type);
~~ ^~~~
%lu
...
make[4]: Nothing to be done for `all'.
make[4]: Nothing to be done for `all-am'.
g++ -DHAVE_CONFIG_H -I. -I.. -I../include -DNCURSES_OPAQUE=0 -I/usr/local/include/SDL -D_GNU_SOURCE=1 -D_THREAD_SAFE -g -O2 -mno-ms-bitfields -MT dosbox.o -MD -MP -MF .deps/dosbox.Tpo -c -o dosbox.o dosbox.cpp
mv -f .deps/dosbox.Tpo .deps/dosbox.Po
g++ -g -O2 -mno-ms-bitfields -o dosbox dosbox.o cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a -lSDL_sound -L/usr/local/lib -lSDLmain -lSDL -Wl,-framework,Cocoa -lncurses -lpng -lz -lX11 -framework OpenGL -framework CoreMIDI -framework AudioUnit -framework AudioToolbox
Making all in include
make[2]: Nothing to be done for `all'.
Making all in docs
make[2]: Nothing to be done for `all'.
Making all in visualc_net
make[2]: Nothing to be done for `all'.
dosbox/dosbox-0.74-3 »
src 디렉터리에 빌드 완료된 dosbox 실행 파일이 위치한다.
dosbox/dosbox-0.74-3 » cd src
dosbox-0.74-3/src » ls
Makefile Makefile.in debug dosbox dosbox.ico fpu hardware libs platform winres.rc
Makefile.am cpu dos dosbox.cpp dosbox.o gui ints misc shell
dosbox를 실행하면 실행된 터미널에 디버깅을 위한 정보가 표시된다.
추가 고려사항
디버거 모드로 진입을 위한 키로 Windows 표준 키보드를 기준으로 Alt+Pause 키가 사용된다. iMac의 기본 키보드에서는 Alt+F16키를 사용하였으나 다른 배열의 키보드에서는 진입 방식이 다를 수 있다.
또한 일부 펑션키를 이용하는 기능은 입력 과정에서 불편함을 느낄 수가 있다. macOS의 키보드 설정을 변경하여 펑션키를 입력할 수 있으나 해당 설정이 불편한 사람도 존재할 수 있다.
소스코드의 debug/debug.c에는 키 입력과 관련된 코드들이 존재하는데 불편하지만 코드를 수정하여 본인에 맞는 환경으로 설정할 수 있어 보인다.
Bit32u DEBUG_CheckKeys(void) {
Bits ret=0;
int key=getch();
if (key>0) {
#if defined(WIN32) && defined(__PDCURSES__)
switch (key) {
case ALT_D:
if (ungetch('D') != ERR) key=27;
break;
...
switch (toupper(key)) {
case 27: // escape (a bit slow): Clears line. and processes alt commands.
key=getch();
if(key < 0) { //Purely escape Clear line
codeViewData.inputStr[0] = 0;
break;
}
switch(toupper(key)) {
case 'D' : // ALT - D: DS:SI
dataSeg = SegValue(ds);
if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esi;
else dataOfs = reg_si;
break;
...
case KEY_HOME: // Home: scroll log page up
DEBUG_RefreshPage(-1);
break;
case KEY_END: // End: scroll log page down
DEBUG_RefreshPage(1);
break;
case KEY_F(6): // Re-enter previous command (f1-f4 generate rubbish at my place)
case KEY_F(3): // Re-enter previous command
// copy prevInputStr back into inputStr
safe_strncpy(codeViewData.inputStr, codeViewData.prevInputStr, sizeof(codeViewData.inputStr));
break;
입력된 명령의 파싱은 ParseCommand 함수의 코드에서 처리된다.
bool ParseCommand(char* str) {
char* found = str;
for(char* idx = found;*idx != 0; idx++)
*idx = toupper(*idx);
found = trim(found);
string s_found(found);
istringstream stream(s_found);
string command;
stream >> command;
string::size_type next = s_found.find_first_not_of(' ',command.size());
if(next == string::npos) next = command.size();
(s_found.erase)(0,next);
found = const_cast<char*>(s_found.c_str());
if (command == "MEMDUMP") { // Dump memory to file
Bit16u seg = (Bit16u)GetHexValue(found,found); found++;
Bit32u ofs = GetHexValue(found,found); found++;
Bit32u num = GetHexValue(found,found); found++;
SaveMemory(seg,ofs,num);
return true;
};
결론
macOS 환경에서 DOSBox Debugger를 빌드하고 동작을 실험하였다. Windows 키보드를 기준으로 설정된 키 값이 불편할 수 있으나 설정 변경과 코드 수정으로 해결 가능한 문제로 보여진다. 아직 디버거의 기능을 제대로 사용해보지 않았기 때문에 편의성은 알 수 없으나 과거 SoftICE에 크게 부족해 보이진 않는다. DOS 프로그램의 디버깅 작업에 DOSBox Debugger가 매우 유용하게 쓰일 수 있을걸로 생각된다.