# 청기백기 소스 버전 비교

1995년 청기백기 DOS 게임의 소스 여러 벌을 비교한 기록이다.
대상은 옛 PC의 `C:\LAN\QB\PCXLIB\`에 있던 데모·준비 코드와 `C:\FLAG\`의 소스다.
2026-10-09에 직접 짠 소스를 `src/`로 옮기면서 청기백기는 최종본 `FF4.BAS`만 `src/flag-game.bas`로 남겼다.
`FLAGV.BAS`, `FLV2.BAS`, `NEWFF4.BAS`는 옮기지 않았고 원래 폴더도 지웠으므로, 이 문서가 그 차이의 기록이다.
`MDATA.BAS`, `MDATAUP.BAS`, `GREADS.BAS`, `PCX-SV.BAS`, `PCXREAD2.BAS`는 `src/`에 새 이름으로 있다.
날짜는 `ls -l --time-style=long-iso`로 본 파일 수정 시각이다.
"추정"이라고 적은 것은 코드나 날짜에서 직접 확인하지 못한 해석이다.

## 요약

- `FLV2.BAS`는 SPR 파일을 만들지 않는다.
  미리 만든 `bd/wd/bu/wu.spr`를 `BLOAD`하고, `DGI.GIF`에서는 보통 자세(`bnor`, `wnor`)만 메모리로 `BLKGET`한다.
  `DOWNBN.GIF`, `UPBN.GIF`를 잘라 SPR로 `BSAVE`하는 코드는 `MDATA.BAS`, `MDATAUP.BAS`에 있다.
- `FLV2.BAS`와 `FLAGV.BAS`는 거의 같은 파일이다.
  둘 다 음성 명령도 판정도 없이, 키를 누르면 깃발이 올라갔다 내려오는 동작만 반복하는 데모다.
- 내용으로 보면 FLV2가 FLAGV보다 한 단계 앞선 상태다.
  그러나 파일 날짜는 FLV2가 1995-04-17로, FF4(1995-02-17)보다 늦다.
- `NEWFF4.BAS`는 FF4의 이전 버전이 아니다.
  날짜가 1996-02-08이고, 퍼즐 작업 때 FF4를 복사해 고치다 만 파일로 보인다(추정).
  SUB 다섯 개가 빠져 있어 이 상태로는 컴파일되지 않는다.
- 최종본은 `FF4.BAS`다.
  1997-01-24에 만든 `FF4.OBJ`의 문자열이 `FF4.BAS`와 일치한다.
- 추정 흐름은 다음과 같다.
  PCX 시험(02-04~05) → GIF 시험과 SPR 제작(02-12~13) → 사운드블라스터 시험(02-15) → FLV2(내용 기준) → FLAGV(02-16) → FF4(02-17) → NEWFF4(1996-02-08, 곁가지).

## 1. 파일별 개요

### 1-1. `LAN/QB/PCXLIB/` (청기백기 준비 코드와 SVGAQB 1.0 배포본)

| 파일 | 크기(B) / 줄 | 날짜 | 역할 |
|------|-------------|------|------|
| `FLV2.BAS` | 7085 / 370 | 1995-04-17 05:00 | 청기백기 데모. 로고+DON2 음성, 깃발 키 시험 루프 |
| `GREADS.BAS` | 1942 / 66 | 1995-02-13 13:27 | `DOWNBN.GIF`를 띄우고 `bu.spr`를 (0,0)에 찍어 보는 SPR 확인용 |
| `MDATA.BAS` | 1943 / 64 | 1995-02-13 13:11 | `DOWNBN.GIF`에서 `bd.spr`, `wd.spr` 생성 (BLKGET+BSAVE) |
| `MDATAUP.BAS` | 1939 / 64 | 1995-02-13 13:28 | `UPBN.GIF`에서 `bu.spr`, `wu.spr` 생성 |
| `PCX.BAS` | 5468 / 249 | 1993-11-07 22:58 | PCX(16/256색) 디코더 모듈. QB 내장 `LINE`/`PSET` 사용. 작성자 표기 없음 |
| `PCXREAD.BAS` | 1602 / 63 | 1995-02-04 02:45 | `PCX.BAS` 시험 프로그램. `SCREEN 13`/`SCREEN 12` |
| `PCXREAD.MAK` | 22 / 2 | 1995-02-04 02:45 | `PCXREAD.BAS` + `PCX.BAS` |
| `PCX-SV.BAS` | 5538 / 250 | 1995-02-05 20:27 | `PCX.BAS`의 그리기를 SVGAQB 호출로 바꾼 판 |
| `PCXREAD2.BAS` | 2006 / 82 | 1995-02-05 20:27 | SVGAQB판 PCX 뷰어 (`RES320`~`RES1024`) |
| `PCXREAD2.MAK` | 26 / 2 | 1995-02-05 20:27 | `PCXREAD2.BAS` + `PCX-SV.BAS` |
| `SVGAQB10.QLB` | 63286 | 1995-02-05 20:26 | 로컬에서 만든 퀵 라이브러리 (배포본에는 LIB만 있음) |
| `SVGAQB10.LIB` / `.BI` | 44057 / 5777 | 1993-06-11 | SVGAQB 1.0 라이브러리와 선언 파일 |
| `SVGAQB10.TXT` | 193617 / 6126 | 1993-06-04 | SVGAQB 매뉴얼. `GIFPUT` 예제가 모든 `gifload`의 원형 |
| `README.1ST` | 6859 / 147 | 1993-06-11 | 라이브러리 소개. 386 이상, 640×480×256에 512KB 필요 |
| `SVGADEMO.BAS` | 34485 / 883 | 1993-06-11 | 배포본 데모 (청기백기와 직접 관련 없음) |
| `SVGAMOD1.BAS` | 28579 / 964 | 1993-06-11 | 배포본 데모 모듈 |
| `SVGAMOD2.BAS` | 41588 / 1206 | 1993-06-11 | 배포본 데모 모듈. `FLAG/`에도 같은 파일이 있음 |
| `SVGADEMO.BI/.MAK/.EXE`, `ORDER.FRM`, `PACKING.LST`, `DRAGON.FNT`, `UTTOWER.GIF` | - | 1993 | 배포본 부속 |

### 1-2. `LAN/QB/` (상위 폴더의 관련 파일)

| 파일 | 크기(B) / 줄 | 날짜 | 역할 |
|------|-------------|------|------|
| `GREAD.BAS` | 1705 / 49 | 1995-02-12 02:43 | `SVGAQB10.TXT`의 GIFPUT 예제를 그대로 옮긴 GIF 뷰어 (파일명을 `INPUT`으로 받음) |
| `GREADS.OBJ` / `GREADS.EXE` | 2236 / 55048 | 1995-02-12 04:46 | `C:\LAN\QB\PCXLIB\GREADS.BAS` 컴파일 결과. 문자열에 `mtitle.dat`와 청기백기 데모 문구가 있음 |
| `MQ.BAT` | 62 | 1995-02-05 22:04 | `LINK /QU`로 LIB에서 QLB를 만드는 배치 |
| `BQLB45.MAP` | 367 | 1995-02-05 20:25 | QLB 링크 부산물. `SVGAQB10.QLB`와 1분 차이 |
| `PCX16.BAS` | 1735 / 64 | 1994-02-22 | 16색 PCX 뷰어 (`SCREEN 12`, 비트플레인 직접 쓰기). 청기백기와 무관 |
| `PCX16F.BAS` | 5271 / 149 | 1994-02-22 | 위 뷰어를 기계어(`CALL ABSOLUTE`)로 가속한 판. 무관 |
| `PCXVIEW.BAS` | 7621 / 281 | 1994-05-31 | 현재 디렉터리의 PCX 목록 뷰어 (`SCREEN 13`). 무관 |
| `PCX2/` | - | 1995-02-04 | 1989년 PCX 라이브러리(`PCX_QB.LIB` 등). 청기백기 소스에서 쓴 흔적 없음 |
| `SVGAQB23/` | - | 1995-04-17 05:01 | SVGAQB 2.3 배포본. 청기백기 소스는 모두 1.0(`SVGAQB10.BI`)을 씀 |

`GREAD.BAS`, `PCX16.BAS`, `PCX16F.BAS`, `PCXVIEW.BAS`는 모두 텍스트 저장본이다(첫 바이트가 `0xFC`가 아님).

### 1-3. `FLAG/` 소스

| 파일 | 크기(B) / 줄 | 날짜 | 역할 |
|------|-------------|------|------|
| `FF4.BAS` | 11957 / 591 | 1995-02-17 02:45 | 최종본. 음성 명령 12개, 판정, 4번 틀리면 종료 |
| `FLAG.BAT` | 23 / 1 | 1995-02-17 02:45 | `qb/l svgaqb10 ff4.bas` |
| `FF4.OBJ` | 18147 | 1997-01-24 00:56 | `FF4.BAS` 컴파일 결과 (문자열이 FF4와 일치) |
| `FLAGV.BAS` | 6857 / 369 | 1995-02-16 01:58 | FF4 직전 단계. FLV2와 거의 같음 |
| `NEWFF4.BAS` | 10910 / 534 | 1996-02-08 06:59 | FF4를 고치다 만 파생본. SUB 5개 없음 |
| `VOICE.BAS` | 2089 / 97 | 1995-02-15 20:58 | 사운드블라스터 DMA로 `DON2.WAV` 재생 시험 |
| `FLAG2.BAS` | 2089 / 97 | 1995-02-15 21:10 | `VOICE.BAS`와 바이트 단위로 같음 |
| `OVERLF.BAS` | 143 / 8 | 1995-02-15 21:46 | `DON2.WAV` 길이(`LOF`)를 `INTEGER` 변수에 넣어 보는 시험 |
| `MKPU.BAS` | 2470 / 102 | 1996-02-08 17:07 | 퍼즐 빈 칸 `BOARD.SPR` 제작 도구 |
| `PUZZLE.BAS` | 5201 / 236 | 1996-02-10 03:57 | 퍼즐 게임 |
| `PUZZLE.EXE` | 56466 | 1996-02-08 07:38 | 퍼즐 실행 파일 |
| `DAT.BAS` | 583 | 1996-02-08 17:37 | QB 4.5 바이너리 저장본. DOSBox-X의 QB로 텍스트 변환해 보니 퍼즐 칸 좌표 `109 + 90 * j`, `124 * i - 105`, `197 + 90 * j`, `17 + 124 * i`를 찍어 보는 계산용 코드다 |
| `SVGAMOD2.BAS`, `SVGAQB10.BI/.LIB/.QLB/.TXT` | - | - | `PCXLIB/`의 같은 이름 파일과 바이트 단위로 같음 |

### 1-4. `FLAG/` 에셋과 기타 파일

| 파일 | 크기(B) | 날짜 | 내용 / 쓰는 소스 |
|------|--------|------|-----------------|
| `DUMP0001.PCX`, `DUMP0003.PCX` | 190444, 192339 | 1995-02-02 21:48 | 야구 게임 화면 캡처. 나무 무늬 UI라 보드 디자인 참고 자료로 추정. 소스 참조 없음 |
| `CHA.THN` | 9480 | 1995-02-07 09:26 | `THNL` 헤더의 데이터. 정체 불명 |
| `MTITLE.DAT` | 78306 | 1995-02-12 04:23 | 실제로는 640×480 GIF87a. 초기 보드 화면. `GREADS.OBJ`(02-12)가 로드 |
| `BLBOA` | 13478 | 1995-02-12 08:02 | 220×270 PCX 칠판 그림. 소스 참조 없음 |
| `NECH2.PCX` | 51388 | 1995-02-13 09:02 | 흰 바탕에 캐릭터 세 자세(보통, 두 깃발 올림, 내림). 깃발 그림 원본으로 추정. 소스 참조 없음 |
| `WAVER.CFG`, `DISK` | 67, 42652 | 1995-02-15 20:43~44 | 녹음 프로그램 설정과 WAV(RIFF)로 추정. 소스 참조 없음 |
| `DON2.WAV` | 50716 | 1995-02-15 20:50 | 오프닝 음성. VOICE, FLAG2, OVERLF, FLV2, FLAGV |
| `BUP.WAV`, `WUP.WAV`, `DON3.WAV` | 8596, 8596, 37170 | 1995-02-15 20:53~21:04 | 녹음 시험으로 추정. 소스 참조 없음 |
| `START.WAV` | 7920 | 1995-02-15 23:29 | 경기 시작음. FF4, NEWFF4 |
| `LOGO.GIF` | 4385 | 1995-02-15 23:34 | 로고. FLV2, FLAGV, FF4, NEWFF4, 퍼즐 |
| `START.VOC` | 0 | 1995-02-16 02:25 | 빈 파일 |
| `B1.WAV`~`B12.WAV` | 7123~9436 | 1995-02-16 07:42~07:53 | 헤더 없는 8비트 PCM 음성 명령. FF4, NEWFF4 |
| `DGI.GIF` | 78335 | 1995-02-16 17:33 | 최종 보드 화면. FLV2, FLAGV, FF4, NEWFF4 |
| `BD.SPR`, `WD.SPR` | 11181 | 1995-02-16 17:35 | 깃발 내린 자세 77×145. BSAVE 길이 11174 |
| `BU.SPR`, `WU.SPR` | 13491 | 1995-02-16 17:35 | 깃발 올린 자세 77×175. BSAVE 길이 13484 |
| `M3.GIF`, `BOARD.SPR`, `BOARD2.PCX` | - | 1996-02 | 퍼즐용 |
| `DON.WAV` | 82461 | 1994-02-20 | 소스 참조 없음 |

`DOWNBN.GIF`, `UPBN.GIF`는 `old-com` 어디에도 없다.
`MTITLE.DAT`와 `DGI.GIF`를 비교하면 머리글이 "프로그램 짠 사람 : 정상혁...1995년 2월"에서 "브니엘고 컴퓨터 서클 돈데크만 : 정상혁"으로 바뀌었다.
캐릭터 배경도 흰색에서 체크무늬로 바뀌었다.
현재 SPR 네 개를 `DGI.GIF` 팔레트로 풀어 보면 배경이 체크무늬다.
따라서 지금 남은 SPR은 체크무늬 배경 그림에서 잘라 낸 것이다.

## 2. 청기백기 버전 흐름

### 2-1. 날짜순 기록

| 날짜 | 파일 | 의미 |
|------|------|------|
| 1995-02-04 02:45 | `PCXREAD.BAS` | PCX 표시 시험 (QB 내장 그래픽) |
| 1995-02-05 20:25~27 | `BQLB45.MAP`, `SVGAQB10.QLB`, `PCX-SV.BAS`, `PCXREAD2.BAS` | SVGAQB 도입. QLB를 만들고 PCX 뷰어를 SVGAQB로 옮김 |
| 1995-02-12 02:43 | `GREAD.BAS` | 매뉴얼의 GIF 예제로 GIF 표시 시험 |
| 1995-02-12 04:23~46 | `MTITLE.DAT`, `GREADS.OBJ/EXE` | 초기 보드 화면을 GIF로 띄워 봄. 데모 문구가 처음 등장 |
| 1995-02-13 13:11~28 | `MDATA.BAS`, `GREADS.BAS`, `MDATAUP.BAS` | 깃발 스프라이트 SPR 제작과 확인 |
| 1995-02-15 20:43~21:46 | `DON2.WAV`, `VOICE.BAS`, `FLAG2.BAS`, `OVERLF.BAS` | 녹음, 사운드블라스터 DMA 재생 시험 |
| 1995-02-15 23:29~34 | `START.WAV`, `LOGO.GIF` | 시작음과 로고 |
| (내용 기준) | `FLV2.BAS` | 게임 틀 + VOICE 코드 합침 |
| 1995-02-16 01:58 | `FLAGV.BAS` | 음성 명령 준비 코드가 조금 들어감 |
| 1995-02-16 07:42~07:53 | `B1~B12.WAV` | 음성 명령 12개 녹음 |
| 1995-02-16 17:33~35 | `DGI.GIF`, SPR 4개 | 최종 보드와 스프라이트 |
| 1995-02-17 02:45 | `FF4.BAS`, `FLAG.BAT` | 최종본 |
| 1995-04-17 05:00 | `FLV2.BAS` 파일 날짜 | 같은 날 05:01에 `SVGAQB23/`이 생김 |
| 1996-02-08 06:57~07:38 | `M3.GIF`, `NEWFF4.BAS`, `PUZZLE.EXE` | 퍼즐 작업 시작 |
| 1997-01-24 00:56 | `FF4.OBJ` | FF4를 다시 컴파일 |

### 2-2. PCXLIB 준비 코드가 가장 앞선다

- `PCXREAD`(02-04) → `PCXREAD2`(02-05) → `GREAD`(02-12) → `GREADS`/`MDATA`(02-13) 순서는 날짜와 내용이 모두 맞는다.
- `GREADS.OBJ`(02-12 04:46)에 이미 `It's demo version of flag game programmed by Jung-Sang-Hyuk` 문구와 `mtitle.dat`가 들어 있다.
  그러므로 청기백기 작업은 늦어도 1995-02-12에 시작됐다.
- `GREADS.BAS`, `MDATA.BAS`, `MDATAUP.BAS`는 `C:\FLAG\...` 경로의 그림을 읽고 `C:\LAN\QB\PCXLIB\SVGAQB10.BI`를 포함한다.
  PCX 시험 코드는 명령줄로 받은 파일을 읽으며 청기백기 그림 경로가 없다.

### 2-3. FLV2는 내용상 FLAGV 바로 앞이다 (추정)

근거는 다음과 같다.

- FLV2에만 있는 `SUB sayvoc2`는 `VOICE.BAS`의 `sayvoc`와 본문이 같다.
  FLAGV와 FF4에는 `sayvoc2`가 없다.
- FLV2 전체에서 `LF`, `SG`, `OF`가 대문자다.
  `VOICE.BAS`도 대문자다.
  QB는 같은 변수 이름의 대소문자를 모듈 전체에서 마지막 입력에 맞춰 바꾼다.
  그래서 VOICE 코드를 붙여 넣은 직후 상태로 보인다(추정).
  FLAGV와 FF4에서는 모두 소문자 `lf`, `sg`, `of`다.
- FLAGV에는 FF4의 음성 미리 읽기 코드의 싹이 있지만 FLV2에는 없다.

  ```basic
    DIM SHARED rd(13) AS LONG
  ...
   OPEN f$ FOR RANDOM AS #1 LEN = 200
  ```

  FF4에서는 이 블록이 `OPEN "c:\flag\b1.wav"` ~ `b12.wav` 열두 벌로 늘어난다.
- 날짜가 반대라는 점은 설명이 필요하다.
  FLV2의 날짜(1995-04-17 05:00:16)는 `SVGAQB23/` 폴더(05:01)와 1분 차이다.
  같은 날 `CLOCK.BAS`(05:59), `S12ASC.BAS`(03:04)도 저장됐다.
  그날 QB 작업 중에 FLV2를 다시 저장했을 가능성이 있다(추정).
- 반대로 FF4 이후에 FLAGV를 정리해 FLV2를 만들었을 가능성도 완전히 없지는 않다.
  FLAGV의 `OPEN f$`(빈 파일 이름) 블록을 지우면 데모가 제대로 돈다는 동기는 있다.
  하지만 그러려면 쓰지 않는 `sayvoc2`를 VOICE에서 다시 붙이고 `rd(13)`까지 지워야 해서 덜 자연스럽다.

### 2-4. FLAGV → FF4

- FLAGV(02-16 01:58)와 FF4(02-17 02:45) 사이에 `B1~B12.WAV`(02-16 오전)가 녹음됐다.
  FF4만 이 파일을 쓴다.
- `DGI.GIF`와 SPR 네 개가 02-16 17:33~35에 다시 저장됐다.
  FLAGV는 이보다 먼저 저장됐지만 이미 `dgi.GIF`와 `bd.spr` 등을 읽는다.
  그래서 이 시각에 그림과 SPR을 다시 만든 것으로 보인다(추정).
- FLAGV의 깃발 좌표는 1픽셀 어긋나 있고 FF4는 SPR을 잘라 낸 좌표로 돌아간다.
  SPR을 다시 만들면서 보정이 필요 없어졌을 수 있다(추정).

### 2-5. NEWFF4는 FF4 뒤의 곁가지다

- 날짜가 1996-02-08 06:59로 FF4보다 1년 늦다.
  2분 전에 퍼즐 그림 `M3.GIF`(06:57)가 저장됐고, 39분 뒤 `PUZZLE.EXE`(07:38)가 만들어졌다.
- NEWFF4의 `gifload`는 `PUZZLE.BAS`, `MKPU.BAS`의 `gifload`와 대소문자만 빼고 같다.
  팔레트를 모두 63(흰색)으로 채운 뒤 그림을 그리고, 그다음에 진짜 팔레트를 적용한다.

  ```basic
              FOR I = 1 TO 768
              MID$(GIFPAL, I, 1) = CHR$(63)
              NEXT I
              PALSET GIFPAL, 0, 255
  ```

- 로고 경로도 퍼즐처럼 상대 경로 `"logo.gif"`다.
- `RUNDMA`, `SAYVOC`, `SBLCMD`, `voplay`, `wdput` SUB가 없는데 여전히 호출한다.
  QB는 SUB를 이름순으로 저장하므로 끝이 잘린 것이 아니라 중간 SUB들이 지워진 것이다.
- `FF4.OBJ`(1997-01-24)에는 `c:\flag\logo.gif`, `ceg>c` 문자열이 있다.
  이것은 NEWFF4가 아니라 FF4.BAS에 맞는 내용이다.
  그러므로 1997년에도 실제로 빌드한 것은 FF4다.

## 3. FLV2와 FLAG 폴더 버전들의 차이

### 3-1. 한눈에 보는 표

| 항목 | FLV2 | FLAGV | FF4 | NEWFF4 |
|------|------|-------|-----|--------|
| 그래픽 라이브러리 | SVGAQB 1.0 | SVGAQB 1.0 | SVGAQB 1.0 | SVGAQB 1.0 |
| INCLUDE 경로 | `c:\lan\qb\pcxlib\svgaqb10.bi` | 같음 | `svgaqb10.bi` (FLAG 폴더 복사본) | `svgaqb10.bi` |
| PCX 루틴 | 없음 | 없음 | 없음 | 없음 |
| 화면 모드 | `RES640` (640×480×256) | 같음 | 같음 | 같음 |
| 그림 로딩 | `gifload` (매뉴얼 예제형) | 같음 | 같음 | 흰 팔레트 먼저, `GIFPUT` 뒤 `PALSET` |
| SPR 처리 | 4개 `BLOAD` | 같음 | 같음 | 같음 |
| 보통 자세 | `DGI.GIF`에서 `BLKGET` (저장 안 함) | 같음 | 같음 | 같음 |
| 깃발 좌표 | bu/bd (416,43)/(416,109), wu/wd (545,43)/(545,109) | bu (416,42), bd (416,108), wu (544,42), wd (545,108) | FLV2와 같음 | FLV2와 같음 |
| 오프닝 | `logo.gif` + `DON2.WAV` 재생 | 같음 | `logo.gif`만 | `logo.gif`만 (상대 경로) |
| 음성 명령 | 없음 (`comvoc`는 빈 틀) | 없음 (`rd(13)`, 빈 `OPEN f$`) | `B1~B12.WAV` 미리 읽음 | FF4와 같으나 재생 SUB 없음 |
| 명령 선택 | 없음 | 없음 | `RANDOMIZE TIMER`, `INT(RND * 12) + 1` | 같음 |
| 키 | A/a, Z/z, `'`, `/`, 종료 `e` | 같음 | 같음 | 같음 |
| 판정 | 없음 | 없음 | 있음 | 있음 |
| 종료 조건 | `e`만 | `e`만 | `e` 또는 `wa% = 4` | 같음 |
| 제한시간 | 없음 | 없음 | `delay / 10`회 루프 | 같음 |
| 효과음 | 없음 | 없음 | 정답 `PLAY "ge"`, 종료 `PLAY "ceg>c"` | 정답 `"ge"`만 |
| 점수, 메뉴 | 없음 | 없음 | 없음 | 없음 |

### 3-2. 사용 라이브러리와 화면

- 네 버전 모두 PCX 루틴을 쓰지 않는다.
  `PCX.BAS` 계열은 준비 단계에서 버려졌다.
- 네 버전 모두 SVGAQB 1.0의 `VIDEOMODEGET`, `RES640`, `GIFGETINFO`, `GIFPUT`, `PALSET`, `BLKGET`, `BLKPUT`을 쓴다.
- 시작 검사(`SUB start`)도 같다.
  `WHICHVGA = 0`, `WHICHMEM < 512`, `WHICHCPU`가 286 또는 86이면 `STOP`한다.
  이어서 `TIME$`가 바뀌는 1초 동안 빈 루프를 돌려 `delay`를 잰다.
- FLV2와 FLAGV는 `delay`를 재기만 하고 쓰지 않는다.
  FF4에서 처음으로 제한시간과 지연에 쓴다.

### 3-3. 그림 로딩과 스프라이트

- FLV2의 스프라이트 처리는 다음이 전부다.

  ```basic
    DEF SEG = VARSEG(bd(0))
    BLOAD "bd.spr", VARPTR(bd(0))
  ...
    gifload "C:\FLAG\dgi.GIF "
    BLKGET 416, 43, 492, 253, bnor(0)
    BLKGET 545, 43, 621, 253, wnor(0)
  ```

- SPR을 만드는 쪽은 `MDATA.BAS`다.

  ```basic
    GIFLOAD "C:\FLAG\DOWNBN.GIF "
  blkget 416, 109, 492, 253, bd(0)
  ...
  BSAVE "bd.spr", VARPTR(bd(0)), 11174
  ```

- `MDATAUP.BAS`는 `UPBN.GIF`에서 `blkget 416, 43, 492, 217`로 올린 자세를 잘라 `13484`바이트로 저장한다.
- 배열 크기는 매뉴얼의 `(가로×세로)/2+3` 공식과 맞는다.
  77×145는 `bd(5586)`, 77×175는 `bu(6741)`, 77×211은 `bnor(8127)`이다.
  BSAVE 길이 11174, 13484는 배열 전체 바이트 수(5587×2, 6742×2)다.
- SPR 파일 이름은 네 버전 모두 상대 경로(`"bd.spr"`)라 현재 디렉터리에서 읽는다.

### 3-4. 키 배정과 입력 흐름

- 키는 네 버전 모두 같다.
  `A`/`a`는 청기 올림, `Z`/`z`는 청기 내림, `'`는 백기 올림, `/`는 백기 내림이다.
  소문자 `e`만 종료 키다.
- FLV2와 FLAGV의 `SUB test`는 자유 연습 루프다.

  ```basic
   FOR I! = 1 TO 40000: NEXT
   IF a$ = "A" OR a$ = "a" OR a$ = "z" OR a$ = "Z" THEN
   bnorput
  ```

  키를 누르면 깃발 그림을 찍고, 고정 횟수 빈 루프 뒤 보통 자세로 되돌린다.
- FF4의 `SUB test`는 한 판씩 진행한다.
  시작음을 내고, 무작위 명령 음성을 재생하고, 재생이 끝날 때까지 기다린 뒤 키 하나를 받는다.

  ```basic
  s = INT(RND * 12) + 1
  comvoc s
  voplay
   WHILE RDMACNT(1) <> 65535
   WEND
  ```

- FF4는 음성이 끝나야 입력 루프에 들어간다.
  음성 도중 누른 키는 BIOS 키 버퍼에 남았다가 곧바로 읽힐 것이다(추정).
- FF4에서 깃발을 되돌리는 시간은 `FOR i = 1 TO delay / 4` 빈 루프, 판정 효과음, `sdelay 20`을 합친 것이다.
  `sdelay`는 VGA 수직 동기(약 60~70Hz) 횟수만큼 기다리는 SVGAQB 함수다.

### 3-5. 음성과 사운드

- 사운드블라스터 코드는 `VOICE.BAS`에서 왔다.
  포트 `220h`, DMA 채널 1, DSP 명령 `D1h`(스피커 켬), `40h`(시간 상수), `14h`(8비트 단일 DMA 출력)을 쓴다.
- 시간 상수는 `a = 256 - (1000000 / 2900)`을 256에 맞춰 돌린 값 167이다.
  이는 약 11,236Hz 재생에 해당해 11025Hz 녹음과 가깝다.
- 파일은 200바이트 레코드로 읽어 400바이트 문자열 배열에 담는다.
  최대 40000바이트만 읽으므로 `DON2.WAV`(50716바이트)는 뒷부분이 잘린다.
- DMA 시작 주소는 버퍼 앞에서 64바이트 뒤(`VARPTR(...) + 64`)다.
  RIFF 헤더를 건너뛰려는 값으로 보인다(추정).
  헤더 없는 `B1~B12.WAV`에서는 앞 64바이트의 소리가 빠진다.
- `OVERLF.BAS`는 `DIM lf AS INTEGER`에 `LOF`를 넣어 본다.
  50716은 INTEGER 범위를 넘으므로 이후 모든 버전이 `lf AS LONG`을 쓰게 된 계기로 보인다(추정).
- FLV2와 FLAGV는 오프닝에서만 소리를 낸다.

  ```basic
    SUB opening
    gifload "c:\flag\logo.gif"
    sayvoc "c:\flag\don2.wav"
  ```

- FF4는 오프닝에서 `sayvoc`를 뺐다.
  대신 `test` 처음에 `SAYVOC "c:\flag\start.wav"`를 부르고, 명령 음성 12개를 `voc1`~`voc12` 배열에 미리 올려 둔다.
- PC 스피커 효과음(`PLAY`)은 FF4에만 있다.
  `gifload` 실패 시 `SOUND 100, 5`는 모든 버전에 있다.

### 3-6. 정답 판정, 틀린 횟수, 종료

- FLV2와 FLAGV에는 판정이 없다.
- FF4의 판정은 다음과 같다.

  ```basic
   IF s = 1 OR s = 2 THEN
    IF a$ = "a" OR a$ = "A" THEN
    wa% = wa% - 1
    PLAY "ge"
  ...
   wa% = wa% + 1
  ...
   IF wa% = 4 THEN GOTO gameover:
  ```

  정답이면 `wa%`를 하나 빼고 무조건 하나 더하므로 그대로다.
  오답이나 시간 초과면 하나 는다.
- 명령 1·2는 `A`, 4·5는 `Z`, 7·8은 `'`, 10·11은 `/`가 정답이다.
  3·6·9·12는 아무 키도 누르지 않아야 정답이다.
- 4번 틀리면 `PLAY "ceg>c"`를 내고 SUB를 끝낸다.
  이후 메인에서 키를 기다린 뒤 텍스트 모드로 돌아가 데모 문구를 찍는다.
- FLV2와 FLAGV의 `comvoc`에는 `s = 1`~`13`의 빈 `IF`가 13개 있다.
  FF4는 12개다.
  13번째 명령이 계획됐다가 빠진 이유는 알 수 없다.

### 3-7. 제한시간과 지연 루프

- FF4의 반응 제한은 `waitime& = delay / 10`회 반복이다.
  반복 한 번마다 `INKEY$`와 비교문을 실행하므로 실제 시간은 CPU 속도와 루프 내용에 따라 다르다.
- FF4는 시간 막대 자리에 칸 5개를 그리고 `cup(1)`~`cup(5)`를 계산한다.

  ```basic
    FOR j = 1 TO 5
    drwbox 4, 37, 90 + j * 44, 94, 134 + j * 44, 100
    NEXT j
  ```

  하지만 `IF i = cup(j) THEN` 안이 비어 있어 막대가 줄어드는 표시는 없다.
- FLV2와 FLAGV의 지연은 `FOR I! = 1 TO 40000` 고정 루프뿐이다.

### 3-8. 점수와 메뉴

- 네 버전 모두 점수, 최고 점수, 맞은 횟수, 남은 기회를 계산하거나 표시하지 않는다.
- 네 버전 모두 메뉴가 없다.
- 이 항목들은 보드 그림(`MTITLE.DAT`, `DGI.GIF`)에만 그려져 있다.
  보드에는 "경기 시작 / 대기실 / 나가기" 메뉴와 빨간 동그라미도 있다.

### 3-9. 파일 경로

| 경로 | FLV2 | FLAGV | FF4 | NEWFF4 |
|------|------|-------|-----|--------|
| 보드 | `C:\FLAG\dgi.GIF ` | 같음 | 같음 | 같음 |
| 로고 | `c:\flag\logo.gif` | 같음 | 같음 | `logo.gif` |
| 오프닝 음성 | `c:\flag\don2.wav` | 같음 | 없음 | 없음 |
| 시작음 | 없음 | 없음 | `c:\flag\start.wav` | 같음 |
| 명령 음성 | 없음 | 없음 | `c:\flag\b1.wav` ~ `b12.wav` | 같음 |
| SPR | `bd.spr` 등 (현재 디렉터리) | 같음 | 같음 | 같음 |

- `dgi.GIF ` 뒤의 공백은 원문 그대로다.
- 준비 코드는 `C:\FLAG\DOWNBN.GIF `, `C:\FLAG\upbn.GIF `, `C:\LAN\QB\PCXLIB\SVGAQB10.BI`를 하드코딩한다.

## 4. PCXLIB 준비 코드별 시험 내용과 최종본에 남은 것

| 파일 | 시험한 것 | 최종본(FF4)에 남은 것 |
|------|----------|---------------------|
| `PCX.BAS` + `PCXREAD.BAS` | PCX 헤더 읽기, RLE 풀기, 256색 팔레트를 `\ 4`로 줄여 `INT 10h`로 설정. `SCREEN 13`/`12` | 없음 |
| `PCX-SV.BAS` + `PCXREAD2.BAS` | 같은 디코더를 SVGAQB(`DRWLINE`, `DRWPOINT`, `SETVIEW`, `FILLVIEW`, `RESTEXT`)로 옮기고 그림 크기에 따라 `RES320`~`RES1024` 선택 | SVGAQB를 쓴다는 결정 자체. 코드는 남지 않음 |
| `GREAD.BAS` (상위 폴더) | 매뉴얼 GIFPUT 예제 그대로 | `gifload`의 본문 전체, `WHICHVGA`/`WHICHMEM` 검사, `RES640`, 끝의 `VIDEOMODESET VMODE` |
| `GREADS.BAS` | 02-12판은 `mtitle.dat`(초기 보드)를 띄움. 02-13판은 `DOWNBN.GIF`를 띄우고 `bu.spr`를 `BLOAD`해 (0,0)에 찍음 | `gifload`를 SUB로 뺀 형태, `DEF SEG`+`BLOAD` 방식, `DIM bu(6741)`, 데모 문구 |
| `MDATA.BAS` | `DOWNBN.GIF`에서 내린 자세 잘라 `bd.spr`, `wd.spr` BSAVE | SPR 파일 자체, 배열 크기 `bd(5586)`, 좌표 (416,109)/(545,109) |
| `MDATAUP.BAS` | `UPBN.GIF`에서 올린 자세 잘라 `bu.spr`, `wu.spr` BSAVE | SPR 파일 자체, 배열 크기 `bu(6741)`, 좌표 (416,43)/(545,43) |
| `SVGADEMO.BAS`, `SVGAMOD*.BAS` | 배포본 데모. 수정 흔적 없음 | 없음 (라이브러리만 사용) |
| `README.1ST`, `SVGAQB10.TXT` | 라이브러리 문서 | 386 요구 사항이 `WHICHCPU` 검사로, BLKGET 배열 공식이 배열 크기로 이어짐 |

- `MDATA.BAS`, `MDATAUP.BAS`의 `GIFLOAD`와 FF4의 `gifload`는 대소문자와 빈 줄 말고는 같다.
  팔레트 값이 63을 넘으면 4로 나누는 처리도 매뉴얼 예제 그대로다.
- 준비 코드의 종료 문구 `It's demo version of flag game programmed by Jung-Sang-Hyuk`는 FF4까지 그대로 남았다.
  `MKPU.BAS`에서만 `It's test of the game programmed by Jung-Sang-Hyuk`로 바뀐다.
- `PCX16.BAS`, `PCX16F.BAS`, `PCXVIEW.BAS`(1994)는 청기백기보다 1년 앞선 일반 PCX 뷰어다.
  청기백기 소스와 공유하는 코드는 없다.

## 5. FF4, NEWFF4, FLAGV 사이의 차이

### 5-1. FLAGV → FF4에서 바뀐 것

- `REM $INCLUDE`가 `'c:\lan\qb\pcxlib\svgaqb10.bi'`에서 `'svgaqb10.bi'`로 바뀌었다.
  FLAG 폴더에 라이브러리를 복사하고 `FLAG.BAT`(`qb/l svgaqb10 ff4.bas`)로 여는 구성이 됐다.
- `RANDOMIZE TIMER`가 추가됐다.
- 빈 `OPEN f$` 블록이 `B1~B12.WAV`를 `voc1`~`voc12`에 읽는 코드 12벌로 바뀌었다.
  길이는 `rd(1)`~`rd(12)`에 둔다.
- `comvoc`가 인자 `s%`를 받아 해당 `voc` 배열의 주소를 `of`, `sg`에 넣도록 채워졌다.
- 오프닝에서 `DON2.WAV` 재생이 빠졌다.
- `test`가 연습 루프에서 게임 루프로 바뀌었다.
  시작음, 무작위 명령, DMA 종료 대기, 제한시간, 판정, `wa%`, 게임 오버가 들어갔다.
- 깃발 좌표가 FLAGV의 1픽셀 보정값에서 SPR 제작 좌표로 돌아갔다.
  `bdput`은 108→109, `buput`은 42→43, `wdput`은 108→109, `wuput`은 (544,42)→(545,43)이다.

### 5-2. FF4 → NEWFF4에서 바뀐 것

- `gifload`가 퍼즐과 같은 방식(흰 팔레트 먼저, 그림 뒤 팔레트 적용)으로 바뀌었다.
- 로고 경로가 `"logo.gif"` 상대 경로가 됐다.
  나머지 경로는 여전히 `c:\flag\...` 절대 경로다.
- 게임 오버의 `PLAY "ceg>c"`가 빠졌다.
- `RUNDMA`, `SAYVOC`, `SBLCMD`, `voplay`, `wdput` SUB와 `DECLARE SUB voplay`가 빠졌다.
  호출은 남아 있으므로 이대로는 "Subprogram not defined"로 컴파일되지 않을 것이다(추정, 실제로 돌려 보지 않음).
- 나머지 차이는 `i`/`I`, `a`/`A` 같은 대소문자뿐이다.
  QB의 이름 대소문자 정규화 때문으로 보인다.

### 5-3. FLAGV와 FLV2의 차이 (참고)

- FLV2에만 `SUB sayvoc2`가 있다(호출되지 않음).
- FLAGV에만 `DIM SHARED rd(13) AS LONG`과 빈 `OPEN f$` 블록이 있다.
  `f$`는 값이 없으므로 FLAGV는 실행 중 파일 이름 오류로 멈췄을 것이다(추정).
- 깃발 좌표가 다르다(3-1 표 참고).
- `LF`/`lf`, `SG`/`sg`, `OF`/`of` 대소문자가 다르다.
- 그 밖의 `test`, `opening`, `start`, `gifload`, 사운드 SUB는 같다.

## 6. 웹 재구현(`flag/flag.js`)과의 관계

- `flag.js`는 첫 주석에 `FF4.BAS` 원작이라고 적혀 있고 규칙도 FF4를 따른다.
  명령 12개와 정답 키, `wa% = 4` 종료, `PLAY "ge"`, `PLAY "ceg>c"`가 FF4와 같다.
- `SPRITE_POS`는 FF4의 `BLKPUT` 좌표(416,43), (416,109), (545,43), (545,109)와 같다.
  FLAGV의 1픽셀 보정 좌표는 따르지 않았다.

### 6-1. 이전 버전에만 있던 동작

| 동작 | 있던 버전 | 웹 버전 |
|------|----------|---------|
| 로고 화면에서 `DON2.WAV` 재생 | FLV2, FLAGV (FF4에서 삭제) | 있음. 로고에서 키를 누를 때 `opening.wav`(DON2) 재생. 원작은 로고를 띄우자마자 재생 |
| 명령 없이 키대로 깃발만 움직이는 연습 루프 | FLV2, FLAGV의 `test` | "대기실"이 비슷하게 동작. 원작 코드를 옮긴 것은 아니고 보드 그림의 메뉴 이름을 쓴 것 |
| 고정 루프(`FOR I! = 1 TO 40000`)로 깃발 되돌리기 | FLV2, FLAGV | 따르지 않음. FF4 방식을 280ms로 흉내 |
| 1픽셀 보정한 깃발 좌표 | FLAGV | 따르지 않음 |
| 그림 표시 전 팔레트를 흰색으로 채우기 | NEWFF4 (퍼즐과 같음) | 해당 없음 |
| 게임 오버 효과음 없음 | NEWFF4 | 따르지 않음 (FF4처럼 재생) |

### 6-2. FF4와 다르게 만든 점 (참고)

- 웹은 음성이 나오는 도중에도 입력을 받는다.
  FF4는 `RDMACNT(1)`로 음성이 끝날 때까지 기다린 뒤 입력 루프에 들어간다.
- 웹은 오답 효과음을 낸다.
  FF4에는 오답 효과음이 없다.
- 메뉴, 점수, 시간 막대 줄어듦, 맞은 횟수·남은 기회 표시, 순위는 원작 어느 버전에도 코드가 없다.
  보드 그림에만 있는 항목을 웹에서 구현한 것이다.

## 7. 확인하지 못한 점

- FLV2가 실제로 언제 작성됐는지는 확인하지 못했다.
  내용은 FLAGV 이전 상태로 보이지만 파일 날짜는 1995-04-17이다.
- `DOWNBN.GIF`, `UPBN.GIF`가 남아 있지 않다.
  그래서 현재 SPR이 어느 그림에서 언제 잘렸는지 직접 대조하지 못했다.
  02-16 17:35에 체크무늬 배경 그림으로 다시 만들었다는 것은 날짜와 배경색에서 나온 추정이다.
- `MDATA.BAS`, `MDATAUP.BAS`를 어느 디렉터리에서 실행했는지 모른다.
  SPR을 현재 디렉터리에 저장하므로 FLAG 폴더에서 실행했다면 날짜가 맞는다.
- 어떤 소스도 DOS 환경(DOSBox 등)에서 실행해 보지 않았다.
  FLAGV의 `OPEN f$` 오류, NEWFF4의 컴파일 실패, 음성 도중 입력의 버퍼링은 코드만 읽고 판단한 것이다.
- FF4의 `delay / 10` 제한시간과 `delay / 4` 지연이 실제 몇 초였는지는 알 수 없다.
- NEWFF4에서 SUB 다섯 개를 지운 이유와, 이 파일로 무엇을 하려 했는지는 알 수 없다.
- `CHA.THN`, `BLBOA`, `NECH2.PCX`, `DUMP*.PCX`의 용도는 그림을 보고 추정했을 뿐이다.
- `PCX.BAS`의 원작자는 파일에 표기가 없어 확인하지 못했다.
- 1997-01-24에 `FF4.OBJ`를 다시 만든 이유는 알 수 없다.
- `FLAG/`에 `SVGAMOD2.BAS`(SVGAQB 데모 모듈)가 복사된 이유는 알 수 없다.
- 음성 명령 12개의 실제 문구는 이번에도 듣지 않았다.
