2048.asm 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. ;
  2. ; 2048 For DOS
  3. ;
  4. ; Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
  5. ;
  6. ; This program is free software: you can redistribute it and/or modify
  7. ; it under the terms of the GNU Affero General Public License as
  8. ; published by the Free Software Foundation, either version 3 of the
  9. ; License, or (at your option) any later version.
  10. ;
  11. ; This program is distributed in the hope that it will be useful,
  12. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ; GNU Affero General Public License for more details.
  15. ;
  16. ; You should have received a copy of the GNU Affero General Public License
  17. ; along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. ;
  19. ; NASM Support for anonymous labels:
  20. %assign __anon_label_num__ 0
  21. %macro @@ 0-1+
  22. ..@__anon_label__ %+ __anon_label_num__ %+ %1
  23. %assign __anon_label_prev__ __anon_label_num__
  24. %assign __anon_label_num__ __anon_label_num__ + 1
  25. %endmacro
  26. %idefine @f ..@__anon_label__ %+ __anon_label_num__
  27. %idefine @b ..@__anon_label__ %+ __anon_label_prev__
  28. org 0x100 ; DOS COM Format Executable
  29. ;**********************************************************************************************************************
  30. ;
  31. ; DEFINITIONS
  32. ;
  33. ;**********************************************************************************************************************
  34. VIDEO_SEGMENT EQU 0xA000
  35. WIDTH EQU 80 ; The width of the screen, in characters
  36. HEIGHT EQU 480 ; The height of the screen, in pixels
  37. FIELD_MARGIN EQU 1
  38. FOUR_THRESHOLD EQU 0x1A1A
  39. FIELD_ORIGIN EQU FIELD_MARGIN * 8 * WIDTH + FIELD_MARGIN
  40. FIELD_SIZE EQU HEIGHT / 8 - 2 * FIELD_MARGIN
  41. ;**********************************************************************************************************************
  42. ; Macro: vsync
  43. ;
  44. ; Parameters: none
  45. ; Returns: nothing
  46. ; Trashes: FLAGS
  47. ; Description: Waits for the vertical retrace interval to start and then end.
  48. ;**********************************************************************************************************************
  49. %macro vsync 0
  50. push ax
  51. push dx
  52. mov dx, 0x3DA
  53. @@: in al, dx ; Loop until the bit becomes set
  54. and al, 0x08
  55. jz @b
  56. @@: in al, dx ; Loop again until it clears
  57. and al, 0x08
  58. jnz @b
  59. pop dx
  60. pop ax
  61. %endmacro
  62. ;**********************************************************************************************************************
  63. ;
  64. ; CODE SEGMENT
  65. ;
  66. ;**********************************************************************************************************************
  67. section code progbits align=2
  68. cpu 286 ; 16-bit Real Mode 286-compatible
  69. bits 16
  70. ;**********************************************************************************************************************
  71. ; Subroutine: start
  72. ;
  73. ; Parameters: none
  74. ; Returns: nothing
  75. ;**********************************************************************************************************************
  76. start: cld ; Clear the direction flag
  77. call uncompress
  78. mov ah, 0x2A ; Get the current date
  79. int 0x21
  80. mov word [random_seed], cx ; Store it as the random seed
  81. mov word [random_seed + 2], dx
  82. mov ah, 0x2C ; Get the current time
  83. int 0x21
  84. xor word [random_seed], cx ; XOR it onto the random seed
  85. xor word [random_seed + 2], dx
  86. push 0x40 ; Load the BDA segment into ES
  87. pop es
  88. mov al, byte [es:0x49] ; Save the current video mode
  89. mov byte [previous_mode], al
  90. mov ax, 0x0012 ; Switch to mode 12h (640x480x4@60Hz)
  91. int 0x10
  92. .restart: push ds ; Load our data segment into ES
  93. pop es
  94. mov ax, 0x1002 ; Load the AC palette
  95. xor bx, bx
  96. mov dx, palette
  97. int 0x10
  98. mov ax, 0x1012 ; Load the DAC palette
  99. mov cx, 16
  100. mov dx, palette_colors
  101. int 0x10
  102. xor ax, ax
  103. mov word [score], ax ; Reset the score
  104. mov word [score + 2], ax
  105. mov byte [score + 4], al
  106. mov cx, 8
  107. mov di, field
  108. rep stosw ; Clear the field
  109. push VIDEO_SEGMENT ; Load the video segment into ES
  110. pop es
  111. mov dx, 0x3C4 ; Write to all planes
  112. mov ax, 0x0F02
  113. out dx, ax
  114. call redraw_score
  115. mov di, FIELD_ORIGIN
  116. xor ax, ax
  117. not ax
  118. .draw_field: mov cx, FIELD_SIZE / 2
  119. rep stosw ; Fill
  120. add di, WIDTH - FIELD_SIZE
  121. cmp di, WIDTH * (HEIGHT - FIELD_MARGIN * 8)
  122. jb .draw_field
  123. mov dx, 0x3C4 ; Write to plane 0
  124. mov ax, 0x0102
  125. out dx, ax
  126. xor ax, ax ; AX = 0x0000
  127. mov di, FIELD_ORIGIN
  128. .draw_vertical: stosw ; Draw 5 vertical bars
  129. %rep 4
  130. add di, 96 / 8
  131. stosw
  132. %endrep
  133. add di, WIDTH - FIELD_SIZE
  134. cmp di, WIDTH * (HEIGHT - FIELD_MARGIN * 8)
  135. jb .draw_vertical
  136. mov di, FIELD_ORIGIN
  137. mov cx, 0x05FF
  138. .draw_horizontal:
  139. times FIELD_SIZE / 2 stosw ; Draw 5 horizontal bars
  140. add di, WIDTH - FIELD_SIZE
  141. sub cl, 0x10
  142. jnc .draw_horizontal
  143. add di, WIDTH * 96
  144. dec ch
  145. jnz .draw_horizontal
  146. call create ; Create an initial square
  147. .turn: call check_over ; Check if the game is over
  148. or ax, ax
  149. jnz .game_over
  150. call create ; Create a new square
  151. call check_over ; Check if the game is over (again!)
  152. or ax, ax ; This is important because creating a new
  153. jnz .game_over ; ... tile can change the game.
  154. .keystroke: xor ah, ah ; Wait for a keystroke
  155. int 0x16
  156. cmp ah, 0x01 ; Escape
  157. jz .quit
  158. @@: cmp ah, 0x48 ; Up Arrow
  159. jnz @f
  160. mov dl, 2
  161. call move_tiles
  162. or al, al
  163. jnz .turn
  164. jmp .keystroke
  165. @@: cmp ah, 0x4B ; Left Arrow
  166. jnz @f
  167. mov dl, 0
  168. call move_tiles
  169. or al, al
  170. jnz .turn
  171. jmp .keystroke
  172. @@: cmp ah, 0x4D ; Right Arrow
  173. jnz @f
  174. mov dl, 1
  175. call move_tiles
  176. or al, al
  177. jnz .turn
  178. jmp .keystroke
  179. @@: cmp ah, 0x50 ; Down Arrow
  180. jnz @f
  181. mov dl, 3
  182. call move_tiles
  183. or al, al
  184. jnz .turn
  185. @@: jmp .keystroke
  186. .game_over: mov ax, 0x1C1C
  187. mov dx, 0xA01C
  188. js @f
  189. mov ax, 0xFFFF
  190. @@: call fade_to_color
  191. vsync
  192. mov si, game_over_bg
  193. mov di, 0x3706
  194. mov cx, 0x8030
  195. mov bl, 1
  196. call draw_pattern
  197. inc bl
  198. mov si, game_over
  199. call draw_pattern
  200. @@: xor ah, ah ; Wait for a reply
  201. int 0x16
  202. cmp ah, 0x15 ; 'Y' pressed
  203. jz .restart
  204. cmp ah, 0x31 ; 'N' pressed
  205. jz .quit
  206. jmp @b
  207. .quit: xor ah, ah
  208. mov al, byte [previous_mode] ; Restore the previous mode
  209. int 0x10
  210. ret ; Exit
  211. ;**********************************************************************************************************************
  212. ; Subroutine: uncompress
  213. ;
  214. ; Parameters: none
  215. ; Returns: nothing
  216. ; Trashes: AX, CX, DX, BX, SI, DI, FLAGS
  217. ; Description: Uncompresses the textures
  218. ;**********************************************************************************************************************
  219. uncompress: mov si, compressed_data
  220. mov di, tiles
  221. mov cx, word [compressed_length]
  222. mov dx, 0xFE00 ; Start with zeros
  223. xor bh, bh
  224. .loop: lodsb ; Read a compressed byte
  225. add al, al ; Fetch the actual run length
  226. mov bl, al
  227. mov ax, dx
  228. mov dx, word [run_length_table + bx]
  229. .append: sahf ; Set CF to the lowest bit of AH
  230. adc al, al ; Push that bit into AL
  231. sub ah, 0x20 ; Less space remains in our bit buffer
  232. jnc @f ; Is it full?
  233. stosb ; Then flush it
  234. @@: dec dx
  235. jnz .append
  236. mov dx, ax
  237. xor dh, 1 ; Toggle the bit we're appending
  238. loop .loop
  239. mov ax, dx ; Check if the buffer has something left
  240. cmp ah, 0xFE
  241. jnc @f
  242. xor dh, dh ; Calculate how many more we need to shift
  243. mov dl, ah
  244. shr dl, 5
  245. inc dl
  246. inc cx ; Make sure it doesn't loop again
  247. jmp .append
  248. @@: ret
  249. ;**********************************************************************************************************************
  250. ; Subroutine: create
  251. ;
  252. ; Parameters: none
  253. ; Returns: nothing
  254. ; Trashes: AX, CX, DX, BX, SI, DI, FLAGS
  255. ; Description: Creates a new tile and adds it to the game.
  256. ;**********************************************************************************************************************
  257. create: push es
  258. push ds
  259. pop es
  260. mov bx, field
  261. mov di, bx
  262. xor al, al
  263. mov cx, 16
  264. repnz scasb
  265. pop es
  266. jz .random_empty
  267. ret
  268. .random_empty: call random ; Get a random number in DX:AX
  269. %rep 4
  270. mov si, ax ; Load the low 4 bits of AX into SI
  271. and si, 0x000F
  272. mov di, dx ; And the low 4 bits of DX into DI
  273. and di, 0x000F
  274. cmp byte [bx + si], 0 ; Is field number SI free?
  275. jz .found_low ; Then use that
  276. cmp byte [bx + di], 0 ; Is field number DI free?
  277. jz .found_high ; Then use that
  278. shr ax, 4 ; If neither, shift both by 4 and try again
  279. shr dx, 4
  280. %endrep
  281. jmp .random_empty ; All random bits exhausted, start over
  282. .found_low: mov di, si ; Make sure the field number is in DI
  283. .found_high: push di ; We'll need it later
  284. mov cl, 1 ; Create a 2 tile there
  285. call random ; Check if it should be a 4
  286. cmp ax, FOUR_THRESHOLD
  287. jae @f
  288. inc cl ; If so, make it a 4
  289. @@: mov byte [bx + di], cl ; Store it in the field array
  290. mov ah, 13 ; Get the appropriate plane mask
  291. sub ah, cl
  292. mov dx, 0x3C4 ; Select the plane mask
  293. mov al, 0x02
  294. out dx, ax
  295. mov bx, di
  296. mov si, word [field_coords + bx + di] ; Get the coordinates of the tile
  297. xor ax, ax
  298. mov dx, 0x0FF0 ; Pattern: 1111000000001111
  299. xor ch, ch
  300. xor bh, bh
  301. %assign i 0
  302. %rep 12
  303. %assign offset 44 * WIDTH + 5 - i * 4 * WIDTH - (i >> 1)
  304. vsync
  305. lea di, [si + offset]
  306. mov bl, (i + 1) << 3 ; The number of lines
  307. @@: mov byte [es:di], dl ; Draw the pattern's first half
  308. inc di
  309. %if i > 1
  310. mov cl, i >> 1
  311. rep stosw ; Draw the area in between
  312. %endif
  313. mov byte [es:di], dh ; Draw the pattern's second half
  314. add di, WIDTH - (i | 1) ; Next scanline
  315. dec bl
  316. jnz @b
  317. xor dx, 0x0FF0 ; Flip the sides of the pattern
  318. %assign i i + 1
  319. %endrep
  320. pop di ; Grab the saved field number
  321. xor al, al
  322. mov ah, byte [field + di] ; Get the value of the field
  323. add ah, ah
  324. add ax, tiles - 0x200
  325. mov si, ax
  326. mov bx, di
  327. mov di, word [field_coords + bx + di] ; Get the video offset
  328. add di, 16 * WIDTH + 2 ; Move 16x16 pixels right and down
  329. mov cx, 0x4008
  330. mov bl, 1
  331. jmp draw_pattern
  332. ;**********************************************************************************************************************
  333. ; Subroutine: random
  334. ;
  335. ; Parameters: none
  336. ; Returns: DX:AX - a 32-bit random number
  337. ; Trashes: FLAGS
  338. ;**********************************************************************************************************************
  339. random: push cx
  340. mov ax, word [random_seed] ; Load the 32-bit random seed into DX:AX
  341. mov dx, word [random_seed + 2]
  342. mov ch, dl ; DX:AX ^= DX:AX << 13
  343. mov cl, ah
  344. shl cx, 5
  345. xor dx, cx
  346. xor ch, ch
  347. mov cl, al
  348. shr cl, 3
  349. xor dx, cx
  350. mov ch, al
  351. xor cl, cl
  352. shl ch, 5
  353. xor ax, cx
  354. mov cx, dx ; DX:AX ^= DX:AX >> 17
  355. shr cx, 1
  356. xor ax, cx
  357. mov cx, dx ; DX:AX ^= DX:AX << 5
  358. shl cx, 5
  359. xor dx, cx
  360. xor ch, ch
  361. mov cl, ah
  362. shr cl, 3
  363. xor dx, cx
  364. mov cx, ax
  365. shl cx, 5
  366. xor ax, cx
  367. mov word [random_seed], ax ; Save DX:AX as the new random seed
  368. mov word [random_seed + 2], dx
  369. pop cx
  370. ret
  371. ;**********************************************************************************************************************
  372. ; Subroutine: build_movement_list
  373. ;
  374. ; Parameters: ES:DI - a pointer to the list that should be filled
  375. ; DL - direction (0 - left, 1 - right, 2 - up, 3 - down)
  376. ; Returns: AX - the size of the list, in bytes
  377. ; Trashes: FLAGS
  378. ; Description: Creates a list of movements. Each entry is 16-bit and contains four 4-bit values that correspond to the
  379. ; new value, old value, new location, and old location, in that order (from MSB to LSB)
  380. ;**********************************************************************************************************************
  381. build_movement_list: push bp ; Setup a stack frame
  382. mov bp, sp
  383. xor ax, ax
  384. times 8 push ax ; Reserve memory for the movement table:
  385. ; Each entry is such that the lower 4 bits
  386. ; keep the new location and the higher 4
  387. ; bits keep the new value for any given
  388. ; tile.
  389. push dx
  390. push bx
  391. push si
  392. mov bx, move_slice_back
  393. test dl, 1 ; Towards the front or back?
  394. jz @f
  395. mov bx, move_slice_front
  396. @@: test dl, 2 ; By row or by column?
  397. jnz .columns
  398. %assign i 0
  399. %rep 4
  400. mov ax, word [field + %+ i] ; Load the entire row into DX:AX
  401. mov dx, word [field + 2 + %+ i]
  402. call bx
  403. and ax, 0xF3F3 ; Store the proper row numbers
  404. and dx, 0xF3F3
  405. %if i > 0
  406. or ax, i | (i << 8)
  407. or dx, i | (i << 8)
  408. %endif
  409. mov word [bp - 16 + %+ i], ax ; Store the changes
  410. mov word [bp - 16 + 2 + %+ i], dx
  411. %assign i i + 4
  412. %endrep
  413. jmp .prepare_list
  414. .columns:
  415. %assign i 0
  416. %rep 4
  417. mov al, byte [field + %+ i] ; Load the entire column into DX:AX
  418. mov ah, byte [field + 4 + %+ i]
  419. mov dl, byte [field + 8 + %+ i]
  420. mov dh, byte [field + 12 + %+ i]
  421. call bx
  422. and ax, 0xFCFC ; Store the proper column numbers
  423. and dx, 0xFCFC
  424. %if i > 0
  425. or ax, i | (i << 8)
  426. or dx, i | (i << 8)
  427. %endif
  428. mov byte [bp - 16 + %+ i], al ; Store the changes
  429. mov byte [bp - 16 + 4 + %+ i], ah
  430. mov byte [bp - 16 + 8 + %+ i], dl
  431. mov byte [bp - 16 + 12 + %+ i], dh
  432. %assign i i + 1
  433. %endrep
  434. .prepare_list: mov bx, di ; Save DI
  435. mov si, -16
  436. .fill_list: mov ah, byte [field + si + 16] ; Read the tile value
  437. or ah, ah
  438. jz @f ; Skip blank tiles
  439. mov dx, si ; Store the current location into AL
  440. and dx, 0x000F
  441. mov al, dl
  442. mov dl, byte [bp + si] ; Read the change entry
  443. shl dx, 4 ; Shift the new location into the top 4 of DL
  444. shl dh, 4 ; and the new value into the top 4 of DH
  445. or ax, dx ; Form the list entry
  446. mov dx, ax ; Make a copy of it
  447. rol dl, 4 ; Swap the old / new values
  448. rol dh, 4
  449. cmp ax, dx ; Did it change it all?
  450. jz @f ; No, so there is no change - skip it
  451. stosw ; Yes, append it to the list
  452. @@: inc si
  453. jnz .fill_list
  454. xchg bx, di ; Restore DI
  455. sub bx, di ; Calculate the list size
  456. mov ax, bx
  457. pop si
  458. pop bx
  459. pop dx
  460. leave
  461. ret
  462. ;**********************************************************************************************************************
  463. ; Subroutine: move_slice_back
  464. ;
  465. ; Parameters: DX:AX - the contents of the row/column
  466. ; Returns: DX:AX - an entry for the movement table
  467. ; Trashes: FLAGS
  468. ; Description: Moves a row or column towards the back.
  469. ;**********************************************************************************************************************
  470. move_slice_back: push cx
  471. push bx
  472. mov cx, ax
  473. mov bx, dx
  474. shl cx, 4 ; Store the values into the upper 4 bits
  475. shl bx, 4 ; and put the numbers into the lower 4 bits
  476. or ch, 0x05 ; Note that the row number and the column
  477. or bx, 0x0F0A ; number are the same at this point
  478. or al, al
  479. jz .second
  480. cmp al, ah ; Are the first two the same?
  481. jnz @f
  482. xor al, al
  483. add ch, 0x10
  484. jmp .third ; We can't merge the second one again
  485. @@: or ah, ah
  486. jnz @f ; There is a tile in between, skip
  487. cmp al, dl ; Are the first and third the same?
  488. jnz @f
  489. xor al, al
  490. add bl, 0x10
  491. jmp .slide ; No more merging
  492. @@: or ah, ah
  493. jnz .second
  494. or dl, dl
  495. jnz .third
  496. cmp al, dh ; Are the first and fourth the same?
  497. jnz .slide ; No, so nothing can be merged
  498. xor al, al
  499. add bh, 0x10
  500. jmp .slide ; No more merging either way
  501. .second: or ah, ah
  502. jz .third
  503. cmp ah, dl ; Are the middle two the same?
  504. jnz @f ; If not, we can try the second and fourth
  505. xor ah, ah
  506. add bl, 0x10
  507. jmp .slide
  508. @@: or dl, dl
  509. jnz .third
  510. cmp ah, dh ; Are the second and fourth the same?
  511. jnz .slide ; If not, we can't merge anything
  512. xor ah, ah
  513. add bh, 0x10
  514. jmp .slide
  515. .third: or dl, dl
  516. jz .slide
  517. cmp dl, dh ; Are the final two the same?
  518. jnz .slide ; If not, nothing can be merged
  519. xor dl, dl
  520. add bh, 0x10
  521. .slide: or al, al ; Is the first blank?
  522. jnz @f
  523. sub ch, 5 ; Move the remaining three one step back
  524. sub bx, 0x0505
  525. @@: or ah, ah
  526. jnz @f
  527. sub bx, 0x0505 ; Move the final two one step back
  528. @@: or dl, dl
  529. jnz @f
  530. sub bh, 5 ; Move the last tile one step back
  531. @@: mov ax, cx
  532. mov dx, bx
  533. pop bx
  534. pop cx
  535. ret
  536. ;**********************************************************************************************************************
  537. ; Subroutine: move_slice_front
  538. ;
  539. ; Parameters: DX:AX - the contents of the row/column
  540. ; Returns: DX:AX - an entry for the movement table
  541. ; Trashes: FLAGS
  542. ; Description: Moves a row or column towards the front.
  543. ;**********************************************************************************************************************
  544. move_slice_front: push cx
  545. push bx
  546. mov cx, ax
  547. mov bx, dx
  548. shl cx, 4 ; Store the values into the upper 4 bits
  549. shl bx, 4 ; and put the numbers into the lower 4 bits
  550. or ch, 0x05 ; Note that the row number and the column
  551. or bx, 0x0F0A ; number are the same at this point
  552. or dh, dh
  553. jz .third
  554. cmp dh, dl ; Are the last two the same?
  555. jnz @f
  556. xor dh, dh
  557. add bl, 0x10
  558. jmp .second ; We can't merge the third one again
  559. @@: or dl, dl
  560. jnz @f ; There is a tile in between, skip
  561. cmp dh, ah ; Are the second and fourth the same?
  562. jnz @f
  563. xor dh, dh
  564. add ch, 0x10
  565. jmp .slide ; No more merging
  566. @@: or dl, dl
  567. jnz .third
  568. or ah, ah
  569. jnz .second
  570. cmp dh, al ; Are the first and fourth the same?
  571. jnz .slide ; No, so nothing can be merged
  572. xor dh, dh
  573. add cl, 0x10
  574. jmp .slide ; No more merging either way
  575. .third: or dl, dl
  576. jz .second
  577. cmp dl, ah ; Are the middle two the same?
  578. jnz @f ; If not, we can try the first and third
  579. xor dl, dl
  580. add ch, 0x10
  581. jmp .slide
  582. @@: or ah, ah
  583. jnz .second
  584. cmp dl, al ; Are the first and the third the same?
  585. jnz .slide ; If not, we can't merge anything
  586. xor dl, dl
  587. add cl, 0x10
  588. jmp .slide
  589. .second: or ah, ah
  590. jz .slide
  591. cmp ah, al ; Are the first two the same?
  592. jnz .slide ; If not, nothing can be merged
  593. xor ah, ah
  594. add cl, 0x10
  595. .slide: or dh, dh ; Is the fourth blank?
  596. jnz @f
  597. add cx, 0x0505 ; Move the first three one step forward
  598. add bl, 5
  599. @@: or dl, dl ; Is the third blank?
  600. jnz @f
  601. add cx, 0x0505 ; Move the first two one step forward
  602. @@: or ah, ah ; Is the second blank?
  603. jnz @f
  604. add cl, 5 ; Move the first tile one step forward
  605. @@: mov ax, cx
  606. mov dx, bx
  607. pop bx
  608. pop cx
  609. ret
  610. ;**********************************************************************************************************************
  611. ; Subroutine: move_tiles
  612. ;
  613. ; Parameters: DL - direction (0 - left, 1 - right, 2 - up, 3 - down)
  614. ; Returns: AX - the number of tiles moved
  615. ; Trashes: CX, DX, BX, SI, DI, FLAGS
  616. ; Description: Moves all the tiles in a certain direction, possibly combining some of them.
  617. ;**********************************************************************************************************************
  618. move_tiles: push bp
  619. mov bp, sp
  620. sub sp, 32 ; Reserve memory for the tiles list
  621. mov di, sp ; Point DI to the list
  622. push es ; Save ES
  623. push ss ; Load the stack segment into ES
  624. pop es
  625. call build_movement_list ; Build the list of movements
  626. pop es ; Restore ES
  627. push ax ; Push the list size
  628. or ax, ax
  629. jnz @f
  630. leave ; If the list is empty, exit early
  631. ret
  632. @@: mov dh, dl ; Calculate the change in position
  633. and dh, 1 ; Get the direction
  634. jnz @f
  635. dec dh ; If it's a 0, it should be -1
  636. @@: mov cl, dl
  637. and cl, 2
  638. shl dh, cl ; Multiply it by 4 if it's vertical
  639. mov di, field
  640. xor bh, bh
  641. mov si, word [bp - 34] ; Get the list size
  642. @@: mov bl, byte [bp - 34 + si] ; Read the location byte
  643. and bl, 0x0F ; Find the old location of this entry
  644. mov byte [bx + di], bh ; Clear the tile
  645. sub si, 2
  646. jnz @b
  647. mov cx, dx ; Save DX
  648. mov dx, 0x3C4
  649. mov ax, 0x0F02 ; Write to all planes
  650. out dx, ax
  651. mov dx, 0x3CE
  652. mov al, 0x05
  653. out dx, al
  654. inc dx
  655. in al, dx
  656. push ax ; Save the old write mode
  657. and al, 0xFE
  658. or al, 0x02 ; Write mode 2
  659. out dx, al
  660. mov dx, cx ; Restore DX
  661. .animation:
  662. %assign i 0
  663. %rep 4
  664. vsync
  665. mov si, word [bp - 34]
  666. .draw_frame %+ i: mov bx, word [bp - 34 + si]
  667. mov ax, bx
  668. rol al, 4
  669. cmp al, bl
  670. jz .next %+ i
  671. and ah, 0x0F
  672. add ah, 2 ; Get the palette index
  673. mov al, ah ; Duplicate it
  674. and bx, 0x000F
  675. add bx, bx
  676. mov di, word [field_coords + bx] ; Get the base coordinates of this field
  677. push dx ; Save DX
  678. cmp dl, 1
  679. jz .right %+ i
  680. cmp dl, 2
  681. jz .up %+ i
  682. cmp dl, 3
  683. jz .down %+ i
  684. .left %+ i: mov cl, 96
  685. %if i < 3
  686. sub di, (i + 1) * 4
  687. %else
  688. sub di, i * 4 + 2
  689. %endif
  690. mov dx, di
  691. @@: times 6 stosw
  692. %if i < 3
  693. mov word [es:di], 0x0F0F ; Field background
  694. mov word [es:di + 2], 0x0F0F
  695. %else
  696. mov word [es:di], 0x0E0E ; Border
  697. %endif
  698. add di, WIDTH - 12
  699. dec cl
  700. jnz @b
  701. jmp .pattern %+ i
  702. .right %+ i: mov cl, 96
  703. %if i < 3
  704. add di, (i + 1) * 4
  705. mov dx, di
  706. @@: mov word [es:di - 4], 0x0F0F ; Field background
  707. mov word [es:di - 2], 0x0F0F
  708. %else
  709. add di, i * 4 + 2
  710. mov dx, di
  711. @@: mov word [es:di - 2], 0x0E0E ; Border
  712. %endif
  713. times 6 stosw
  714. add di, WIDTH - 12
  715. dec cl
  716. jnz @b
  717. jmp .pattern %+ i
  718. .up %+ i: mov cl, 96
  719. %if i < 3
  720. sub di, 32 * (i + 1) * WIDTH
  721. %else
  722. sub di, (32 * i + 16) * WIDTH
  723. %endif
  724. mov dx, di
  725. @@: times 6 stosw
  726. add di, WIDTH - 12
  727. dec cl
  728. jnz @b
  729. push ax
  730. %if i < 3
  731. mov ax, 0x0F0F ; Field background
  732. mov cl, 32
  733. %else
  734. mov ax, 0x0E0E ; Border
  735. mov cl, 16
  736. %endif
  737. @@: times 6 stosw
  738. add di, WIDTH - 12
  739. dec cl
  740. jnz @b
  741. pop ax
  742. jmp .pattern %+ i
  743. .down %+ i: add di, i * 32 * WIDTH
  744. push ax
  745. %if i < 3
  746. lea dx, [di + 32 * WIDTH]
  747. mov ax, 0x0F0F ; Field background
  748. mov cl, 32
  749. %else
  750. lea dx, [di + 16 * WIDTH]
  751. mov ax, 0x0E0E ; Border
  752. mov cl, 16
  753. %endif
  754. @@: times 6 stosw
  755. add di, WIDTH - 12
  756. dec cl
  757. jnz @b
  758. pop ax
  759. mov cl, 96
  760. @@: times 6 stosw
  761. add di, WIDTH - 12
  762. dec cl
  763. jnz @b
  764. .pattern %+ i: mov di, dx
  765. pop dx ; Restore DX
  766. add di, 16 * WIDTH + 2
  767. add ah, ah ; Shift the palette entry to the left
  768. xor al, al ; by 9 places, in total
  769. add ax, tiles - 0x600 ; Get the tile address from that
  770. push cx
  771. push si
  772. mov cx, 0x4008
  773. mov si, ax
  774. mov bl, 1
  775. call draw_pattern
  776. pop si
  777. pop cx
  778. .next %+ i: sub si, 2
  779. jnz .draw_frame %+ i
  780. %assign i i + 1
  781. %endrep
  782. vsync
  783. xor cx, cx ; Count the number of updates
  784. mov si, word [bp - 34] ; Get the list size
  785. .update_loop: mov bx, word [bp - 34 + si] ; Read an entry from the list
  786. mov ax, bx ; Make a copy of it
  787. rol al, 4 ; Swap old/new
  788. cmp al, bl ; Did it change?
  789. jz @f ; If not, check if there was a merge
  790. add byte [bp - 34 + si], dh ; Update the table entry
  791. inc cx ; Increase the number of updates
  792. jmp .update_next
  793. @@: rol ah, 4 ; Swap old/new
  794. cmp ah, bh ; Did it change?
  795. jz .update_next ; If not, skip this
  796. push si ; Save SI
  797. mov al, bh ; Save the value in AL
  798. and ax, 0x0FF0 ; Take the new values of AL and AH
  799. or al, ah ; and store them in one byte
  800. mov byte [bp - 34 + si + 1], al ; Update the entry
  801. call increase_score
  802. and bx, 0x000F ; Extract the tile location
  803. add bx, bx
  804. mov di, word [field_coords + bx] ; Get the coordinates of the tile
  805. and ax, 0x0F00 ; Extract the new tile value
  806. mov si, ax ; Point SI to the pattern for this value
  807. add si, si
  808. add si, tiles - 0x200
  809. add ah, 2 ; Convert it into a palette index
  810. mov al, ah ; Duplicate it
  811. mov bx, di ; Save DI
  812. push cx ; Save CX
  813. mov cx, 96
  814. @@: times 6 stosw ; Redraw the square
  815. add di, WIDTH - 12
  816. loop @b
  817. lea di, [bx + 16 * WIDTH + 2] ; Restore DI and move it
  818. mov cx, 0x4008
  819. mov bl, 1
  820. call draw_pattern
  821. pop cx ; Restore CX
  822. pop si ; Restore SI
  823. .update_next: sub si, 2
  824. jnz .update_loop
  825. jcxz @f
  826. jmp .animation
  827. @@: mov dx, 0x3CE
  828. pop ax ; Restore the old write mode
  829. mov ah, al
  830. mov al, 0x05
  831. out dx, ax
  832. mov ax, word [bp - 34] ; Get the list size
  833. mov si, ax
  834. mov di, field
  835. .commit_changes: mov bx, word [bp - 34 + si] ; Read the whole entry
  836. shr bx, 4 ; Discard the old values
  837. mov dh, bh ; Get the new tile value
  838. and bx, 0x000F ; Find the new location
  839. mov dl, byte [bx + di] ; Read the value that is there
  840. cmp dl, dh ; Make sure it's not smaller
  841. jae @f
  842. mov byte [bx + di], dh ; And store the new tile value there
  843. @@: sub si, 2
  844. jnz .commit_changes
  845. shr al, 1 ; Return the number of list entries
  846. leave
  847. ret
  848. ;**********************************************************************************************************************
  849. ; Subroutine: draw_pattern
  850. ;
  851. ; Parameters: DS:SI - The pattern to write
  852. ; ES:DI - Destination (in video memory)
  853. ; CH - the pattern height, in pixels
  854. ; CL - the pattern width, in characters
  855. ; BL - the palette entry to use
  856. ; Returns: nothing
  857. ; Trashes: FLAGS
  858. ; Description: Draws a pattern on the screen
  859. ;**********************************************************************************************************************
  860. draw_pattern: pusha
  861. mov dx, 0x3C4
  862. mov ax, 0x0F02 ; Write to all planes
  863. out dx, ax
  864. mov dx, 0x3CE
  865. mov ah, bl
  866. xor al, al
  867. out dx, ax
  868. mov al, 0x05
  869. out dx, al
  870. inc dx
  871. in al, dx
  872. push ax ; Save the old write mode
  873. or al, 3 ; Write mode 3
  874. out dx, al
  875. xor bh, bh
  876. mov bl, cl
  877. .pattern_line: mov cl, bl
  878. @@: mov al, byte [es:di] ; Load the latch register
  879. movsb ; Write the bit pattern
  880. dec cl
  881. jnz @b
  882. add di, WIDTH
  883. sub di, bx
  884. dec ch
  885. jnz .pattern_line
  886. pop ax ; Restore the old write mode
  887. out dx, al
  888. popa
  889. ret
  890. ;**********************************************************************************************************************
  891. ; Subroutine: check_over
  892. ;
  893. ; Parameters: none
  894. ; Returns: AX - zero if the game is still in progress, positive if the player won, negative if the player lost
  895. ; Trashes: FLAGS
  896. ;**********************************************************************************************************************
  897. check_over: push bp
  898. mov bp, sp
  899. sub sp, 32 ; Allocate space for a dummy list
  900. push es
  901. push cx
  902. push dx
  903. push di
  904. push ss ; Load the stack segment into ES
  905. pop es
  906. mov di, field
  907. mov al, 11 ; We're searching for the 2048 tile
  908. mov cx, 16
  909. repnz scasb ; Search
  910. jnz @f ; Skip the next part if not found
  911. mov ax, 1 ; The player won!
  912. jmp .leave
  913. @@: xor dl, dl ; Check if moves are still possible
  914. lea di, [bp - 32]
  915. %rep 4
  916. call build_movement_list
  917. or ax, ax
  918. jz @f
  919. xor ax, ax ; Not over yet
  920. jmp .leave
  921. @@: inc dl ; Try another direction
  922. %endrep
  923. mov ax, -1 ; The player lost
  924. .leave: pop di
  925. pop dx
  926. pop cx
  927. pop es
  928. leave
  929. ret
  930. ;**********************************************************************************************************************
  931. ; Subroutine: increase_score
  932. ;
  933. ; Parameters: AL - the value of the newly formed tile
  934. ; Returns: nothing
  935. ; Trashes: FLAGS
  936. ;**********************************************************************************************************************
  937. increase_score: pusha
  938. push es
  939. push ds ; Load the data segment into ES
  940. pop es
  941. and ax, 0x000F ; Sanitize the input and clear AH
  942. dec al ; Make AL 0-based
  943. shl al, 2
  944. mov bx, ax
  945. mov dx, word [bcd_values + bx] ; Load the number we should add
  946. mov bx, word [bcd_values + bx + 2]
  947. mov si, score + 4
  948. mov di, si
  949. std ; Go backwards
  950. lodsb
  951. add al, bh
  952. aaa
  953. stosb
  954. lodsb
  955. adc al, bl
  956. aaa
  957. stosb
  958. lodsb
  959. adc al, dh
  960. aaa
  961. stosb
  962. lodsb
  963. adc al, dl
  964. aaa
  965. stosb
  966. lodsb
  967. adc al, 0
  968. aaa
  969. stosb
  970. cld ; The direction flag should be kept clear
  971. pop es
  972. call redraw_score
  973. popa
  974. ret
  975. ;**********************************************************************************************************************
  976. ; Subroutine: redraw_score
  977. ;
  978. ; Parameters: none
  979. ; Returns: nothing
  980. ; Trashes: AX, CX, DX, BX, SI, DI, FLAGS
  981. ;**********************************************************************************************************************
  982. redraw_score: mov si, score
  983. mov di, segment_coords
  984. mov bx, segment_display
  985. mov cx, 4
  986. xor dl, dl
  987. .draw_digit: mov ah, 1 ; Current bit
  988. lodsb
  989. or al, al ; Is this digit a zero?
  990. jnz @f ; Always print non-zeros
  991. jcxz @f ; Always print the rightmost digit
  992. or dl, dl ; Have we written a digit already?
  993. jz .draw_segments ; This is a leading zero, don't draw it
  994. @@: or dl, 1 ; Memorize that we have written one
  995. xlatb ; Convert the digit into a display mask
  996. .draw_segments: push cx
  997. push bx
  998. push si
  999. push di
  1000. xor bl, bl
  1001. test al, ah ; Should we print or clear?
  1002. jz @f
  1003. inc bl ; Print
  1004. @@: mov di, word [di] ; Get the coordinates of this segment
  1005. mov cx, 0x0804 ; Horizontal segments are 32x8
  1006. mov si, horizontal_segment
  1007. test ah, 0x36 ; Should it be vertical instead?
  1008. jz @f
  1009. mov cx, 0x2001 ; Vertical segments are 8x32
  1010. mov si, vertical_segment
  1011. @@: call draw_pattern
  1012. pop di
  1013. pop si
  1014. pop bx
  1015. pop cx
  1016. add di, 2 ; Move to the next segment position
  1017. add ah, ah
  1018. jns .draw_segments ; Repeat until we've exhausted all 7
  1019. sub cx, 1 ; Go to the next digit
  1020. jnc .draw_digit
  1021. ret
  1022. ;**********************************************************************************************************************
  1023. ; Subroutine: fade_to_color
  1024. ;
  1025. ; Parameters: AL - the red color channel of the destination color
  1026. ; AH - the green color channel of the destination color
  1027. ; DL - the blue color channel of the destination color
  1028. ; DH - how much should we fade
  1029. ; Returns: nothing
  1030. ; Trashes: AX, CX, DX, BX, SI, DI, FLAGS
  1031. ;**********************************************************************************************************************
  1032. fade_to_color: push bp
  1033. mov bp, sp
  1034. sub sp, 120 ; Reserve memory
  1035. push es ; Save ES
  1036. push ss ; Load the stack segment into ES
  1037. pop es
  1038. shl ax, 2 ; Make the color channels 8-bit
  1039. shl dl, 2
  1040. mov word [bp - 42], ax ; Save them
  1041. mov byte [bp - 40], dl
  1042. mov si, palette_colors + 9
  1043. lea di, [bp - 120]
  1044. mov cx, 39
  1045. @@: lodsb ; Read a 6-bit color value
  1046. mov ah, al ; Shift it left 8 bits and make it 14-bit
  1047. xor al, al
  1048. stosw
  1049. loop @b
  1050. mov ch, dh
  1051. shr ch, 2 ; We need our counter to be 6-bit
  1052. .frame: vsync
  1053. lea si, [bp - 120]
  1054. lea di, [bp - 39]
  1055. mov cl, 39
  1056. @@: lodsw ; Read a 14-bit color value
  1057. mov al, ah
  1058. stosb ; Store the top 6 bits into the palette
  1059. dec cl
  1060. jnz @b
  1061. push cx
  1062. mov ax, 0x1012
  1063. mov cx, 13
  1064. lea dx, [bp - 39]
  1065. mov bx, 3
  1066. int 0x10 ; Change the DAC palette
  1067. pop cx
  1068. mov si, palette_colors + 9
  1069. lea bx, [bp - 120]
  1070. mov cl, 13
  1071. xor dh, dh
  1072. @@:
  1073. %assign i 0
  1074. %rep 3
  1075. mov dl, byte [bp - 42 + i] ; Load the 8-bit destination color
  1076. xor ah, ah
  1077. lodsb ; Load the 6-bit source color
  1078. shl al, 2 ; Make it 8-bit
  1079. sub ax, dx ; Get the difference between the colors
  1080. sub word [es:bx + i * 2], ax ; Subtract it from the 14-bit color
  1081. %assign i i + 1
  1082. %endrep
  1083. add bx, 6
  1084. dec cl
  1085. jnz @b
  1086. dec ch
  1087. jnz .frame
  1088. pop es
  1089. leave
  1090. ret
  1091. ;**********************************************************************************************************************
  1092. ;
  1093. ; DATA SEGMENT
  1094. ;
  1095. ;**********************************************************************************************************************
  1096. section data progbits align=2 follows=code
  1097. palette: DB 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
  1098. palette_colors: DB 0x33, 0x33, 0x33 ; Background
  1099. DB 0x08, 0x08, 0x08 ; Text
  1100. DB 0x3F, 0x3F, 0x3F ; White
  1101. DB 0x3B, 0x39, 0x36 ; 2
  1102. DB 0x3B, 0x38, 0x32 ; 4
  1103. DB 0x3C, 0x2C, 0x1E ; 8
  1104. DB 0x3E, 0x3D, 0x3C ; 16
  1105. DB 0x3D, 0x25, 0x18 ; 32
  1106. DB 0x3D, 0x17, 0x0E ; 64
  1107. DB 0x3B, 0x33, 0x1C ; 128
  1108. DB 0x3B, 0x33, 0x18 ; 256
  1109. DB 0x3B, 0x32, 0x14 ; 512
  1110. DB 0x3B, 0x31, 0x0F ; 1024
  1111. DB 0x3B, 0x30, 0x0B ; 2048
  1112. DB 0x20, 0x20, 0x20 ; Field border
  1113. DB 0x2A, 0x2A, 0x2A ; Field background
  1114. field_coords: DW 0x0783, 0x0791, 0x079F, 0x07AD
  1115. DW 0x2A83, 0x2A91, 0x2A9F, 0x2AAD
  1116. DW 0x4D83, 0x4D91, 0x4D9F, 0x4DAD
  1117. DW 0x7083, 0x7091, 0x709F, 0x70AD
  1118. bcd_values: DB 0, 0, 0, 2
  1119. DB 0, 0, 0, 4
  1120. DB 0, 0, 0, 8
  1121. DB 0, 0, 1, 6
  1122. DB 0, 0, 3, 2
  1123. DB 0, 0, 6, 4
  1124. DB 0, 1, 2, 8
  1125. DB 0, 2, 5, 6
  1126. DB 0, 5, 1, 2
  1127. DB 1, 0, 2, 4
  1128. DB 2, 0, 4, 8
  1129. segment_display: DB 0x3F, 0x06, 0x5B, 0x4F, 0x66 ; Seven-segment display bits
  1130. DB 0x6D, 0x7D, 0x07, 0x7F, 0x6F
  1131. segment_coords: DW 0x035C, 0x03FF, 0x0CBF, 0x14DC, 0x0CBC, 0x03FC, 0x0C1C
  1132. DW 0x0360, 0x0403, 0x0CC3, 0x14E0, 0x0CC0, 0x0400, 0x0C20
  1133. DW 0x0364, 0x0407, 0x0CC7, 0x14E4, 0x0CC4, 0x0404, 0x0C24
  1134. DW 0x0368, 0x040B, 0x0CCB, 0x14E8, 0x0CC8, 0x0408, 0x0C28
  1135. DW 0x036C, 0x040F, 0x0CCF, 0x14EC, 0x0CCC, 0x040C, 0x0C2C
  1136. horizontal_segment: DB 0x01, 0xFF, 0xFF, 0x80
  1137. DB 0x03, 0xFF, 0xFF, 0xC0
  1138. DB 0x07, 0xFF, 0xFF, 0xE0
  1139. DB 0x0F, 0xFF, 0xFF, 0xF0
  1140. DB 0x0F, 0xFF, 0xFF, 0xF0
  1141. DB 0x07, 0xFF, 0xFF, 0xE0
  1142. DB 0x03, 0xFF, 0xFF, 0xC0
  1143. DB 0x01, 0xFF, 0xFF, 0x80
  1144. vertical_segment: DB 0x00, 0x00, 0x00, 0x00
  1145. DB 0x10, 0x38, 0x7C, 0xFE
  1146. DB 0xFE, 0xFE, 0xFE, 0xFE
  1147. DB 0xFE, 0xFE, 0xFE, 0xFE
  1148. DB 0xFE, 0xFE, 0xFE, 0xFE
  1149. DB 0xFE, 0xFE, 0xFE, 0xFE
  1150. DB 0xFE, 0x7C, 0x38, 0x10
  1151. DB 0x00, 0x00, 0x00, 0x00
  1152. run_length_table: DW 0x005A, 0x000B, 0x0031, 0x0013, 0x002B, 0x0018, 0x0027, 0x001A
  1153. DW 0x0024, 0x001E, 0x0021, 0x0020, 0x001F, 0x0022, 0x001D, 0x0023
  1154. DW 0x001B, 0x0025, 0x000F, 0x0007, 0x0010, 0x0019, 0x000E, 0x000A
  1155. DW 0x000D, 0x000C, 0x0017, 0x0009, 0x0033, 0x0032, 0x0030, 0x002F
  1156. DW 0x0011, 0x002E, 0x002D, 0x0012, 0x0015, 0x00AE, 0x002C, 0x0014
  1157. DW 0x002A, 0x0016, 0x0029, 0x0001, 0x0028, 0x0002, 0x0003, 0x0026
  1158. DW 0x0004, 0x0005, 0x0006, 0x0008, 0x001C, 0x0034, 0x0347, 0x0036
  1159. DW 0x0596, 0x05B8, 0x0035, 0x0773, 0x0990, 0x098B, 0x0A49, 0x0B4B
  1160. DW 0x14DB, 0x00BE, 0x00A7, 0x00B5, 0x007E, 0x00B1, 0x007C, 0x007B
  1161. DW 0x00AC, 0x0079, 0x00AB, 0x0078, 0x00AA, 0x0077, 0x00A9, 0x0076
  1162. DW 0x00A8, 0x0075, 0x0074, 0x0037, 0x005F, 0x0046, 0x3E20, 0x0073
  1163. DW 0x00F4, 0x008E, 0x008F, 0x00C3, 0x0063, 0x00DE, 0x0062, 0x0141
  1164. DW 0x0142, 0x0143, 0x0FE0, 0x00BA, 0x00A3, 0x007A, 0x00AD, 0x00A6
  1165. DW 0x00A5, 0x0072, 0x00A4, 0x0071, 0x0070, 0x005B, 0x0042, 0x381C
  1166. DW 0x006F, 0x00F0, 0x008A, 0x00BD, 0x005E, 0x00D6, 0x00D7, 0x013D
  1167. DW 0x013E, 0x013F, 0x00CB, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
  1168. compressed_length: DW 5701
  1169. compressed_data: incbin 'textures.bin'
  1170. ;**********************************************************************************************************************
  1171. ;
  1172. ; BSS SEGMENT
  1173. ;
  1174. ;**********************************************************************************************************************
  1175. section bss nobits align=2 follows=data
  1176. previous_mode: RESB 1
  1177. random_seed: RESD 1
  1178. field: RESB 16 ; 4x4 field
  1179. score: RESB 5 ; 5-digit unpacked BCD
  1180. tiles: RESB 11 * 8 * 64
  1181. game_over: RESB 48 * 128
  1182. game_over_bg: RESB 48 * 128