From 069a91674451b9aa2a4ba17dfd923740b37e64b3 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Tue, 8 Sep 2020 17:39:51 +0200 Subject: [PATCH] Update engine to 1.0.0 Including a lot of cleanup + bugfixes, more features, and an example program --- .gitmodules | 3 + LICENSE | 2 +- README.md | 12 +- example/.gitignore | 10 + example/border_bottom.png | Bin 0 -> 636 bytes example/border_top.png | Bin 0 -> 7010 bytes example/border_vert.png | Bin 0 -> 6280 bytes example/build.sh | 17 + example/button.png | Bin 0 -> 749 bytes example/fonts/BaseSeven.png | Bin 0 -> 2003 bytes example/fonts/BaseSevenBold_vx8.png | Bin 0 -> 1599 bytes example/fonts/optix.png | Bin 0 -> 1431 bytes example/fonts/optixBold.png | Bin 0 -> 1542 bytes example/hardware.inc | 1 + example/main.asm | 620 ++++++++ example/vwf_config.asm | 42 + make_font.py | 44 + vwf.asm | 2219 +++++++++++++++++---------- 18 files changed, 2134 insertions(+), 836 deletions(-) create mode 100644 .gitmodules create mode 100644 example/.gitignore create mode 100644 example/border_bottom.png create mode 100644 example/border_top.png create mode 100644 example/border_vert.png create mode 100755 example/build.sh create mode 100644 example/button.png create mode 100644 example/fonts/BaseSeven.png create mode 100644 example/fonts/BaseSevenBold_vx8.png create mode 100644 example/fonts/optix.png create mode 100644 example/fonts/optixBold.png create mode 160000 example/hardware.inc create mode 100644 example/main.asm create mode 100644 example/vwf_config.asm create mode 100755 make_font.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..179f57c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "example/hardware.inc"] + path = example/hardware.inc + url = git@github.com:gbdev/hardware.inc.git diff --git a/LICENSE b/LICENSE index e03a22a..5267787 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Eldred Habert +Copyright (c) 2018-2020 Eldred Habert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9966fa0..3669103 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # gb-vwf -A Variable-Width Font engine for the Game Boy. +A very powerful Variable-Width Font engine for the Game Boy & Game Boy Color. + +For detailed instructions, including installation and usage, please check [the wiki](https://github.com/ISSOtm/gb-vwf/wiki) out. + +Bugs can be checked out and reported on [the issue tracker](https://github.com/ISSOtm/gb-vwf/issues), or by bugging me on the GBDev Discord, which you can find [here](https://gbdev.io). + +## Licensing + +The VWF engine itself is licensed under the MIT license; the example program is public domain, assets included. + +According to US law, fonts cannot be copyrighted. You can find some [here](https://github.com/pinobatch/bitmap-fonts/tree/master/vwf). diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..226537f --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,10 @@ +*.vwf +*.2bpp +*.o +*.gb +*.sym +*.map +charmap.asm +*.sn* +*.sav +*.srm diff --git a/example/border_bottom.png b/example/border_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..c0436adab924258237050569e337b1f8c4e93285 GIT binary patch literal 636 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 charmap.asm + +rgbgfx -h -o button.2bpp button.png +rgbgfx -o border_top.2bpp border_top.png +rgbgfx -o border_vert.2bpp border_vert.png +rgbgfx -o border_bottom.2bpp border_bottom.png +rgbasm -Wall -Wextra -o main.o main.asm +rgblink -d -n vwf.sym -m vwf.map -o vwf.gb vwf_config.o main.o +rgbfix -v -p 0xff -m 0x11 vwf.gb +rm fonts/*.vwf charmap.asm button.2bpp border_top.2bpp border_vert.2bpp border_bottom.2bpp main.o vwf_config.o diff --git a/example/button.png b/example/button.png new file mode 100644 index 0000000000000000000000000000000000000000..f82cbffc8bf22e1557f9e1d0cd3200fb4463c07b GIT binary patch literal 749 zcmVEX>4Tx04R}tkv&MmKp2MKriw)>4(%W!lA$_T5EXIMDionYs1;guFuC*(nlvOS zE{=k0!NH%!s)LKOt`4q(Aov5~32Q=L_ z)5(OG&8><(uLxln5nAIivy3@OO2K!0-6O#FyExDCulsZKsdx%ki;UEAVGwJ8p^1^Mx1t?6borOPx$x;UB5&wgKI{Wr-O{;%DeI;_MJLpV{0000CP)t-s|NsB0s;X60RR9100W(`P00009 za7bBm001r{001r{0eGc9b^rhX2XskIMF->v9~KZ0Hq|1_0000PbVXQnLvL+uWo~o; zLvm$dbY)~9cWHEJAV*0}P*;Ht7XSbNzez+vR2b7^U{K&-fB*{}D8p_Klwtn|8$*Bv z%21JjGC(@D_WxS})@lF${~w?z3z)$IlIH*a{{vVZ^Z)+`6zu;1E&TtV!OH&68nAZz z{r^CUK0tIbFdu+u00EE&h{Zq+NKUbbXkcKzHvIrd0|RelY5`cf%gKcDL z4fFl)2NyzzoJ(3oR`}DY00000NkvXXu0mjf!E-+r literal 0 HcmV?d00001 diff --git a/example/fonts/BaseSeven.png b/example/fonts/BaseSeven.png new file mode 100644 index 0000000000000000000000000000000000000000..9276d2b8a30e8105d0a146e7cdb59d68e8a225f3 GIT binary patch literal 2003 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 literal 0 HcmV?d00001 diff --git a/example/fonts/optix.png b/example/fonts/optix.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb06a9e2e460070483321f96f3a7c481db1209b GIT binary patch literal 1431 zcmV;I1!($-P)Px)QAtEWRCt{2oZ*feAq<4sDDVHu^hbIuS=%4>gpICJty7h(u^BKJ&vMtJ_kJ8l zZ@u;QUw*$Fz5fL2Fv<}^P}v4H@JFBmD|R>K4N_B@Cwc0 z_HJ$jeeG2-0lEXmXdpR!wo44P1ly6&2r5}#L&>|Xwr`2{kyak;Oq^wkv$QI?O|qON z0dgBdGwTqL!*lzPO)|EwoIA`WMqmROD**S+%8l{33>>wkJ527wW3+^Wp6Y{8z0Fz_m@PKXe*OP!gtO5KM+ zN=yP;%3cmGd)C&j(2*>QUvnGuB0rKDv=*}{&2n<9&NcI}@ip)8JCeQ+yJ&x{YBac^ zuX8;ydpqWj(-KT`zT>^={CoUQ+d33z@HLf2fFf1V+qt-bLB2nK+Yik7rsuGW9$MP{ zFj;Fg)vop$F_Inbl?NY{TBWz$YFUDpI^vV*mz-PCpr}e51#W54u@#48Z(m^LHco0w z>tnRHRzGi4!g0AO{Vy{ut`K$qtdahD(NM{2=;PS2@YMiyxLsVI$+>#uVFM7 z%M2zXtjcnI?Cs8##RaDD{${-6sJ^v=6cGt*K9Fh(UD^sPFw={lZ_Y;+>fM)Kg33^t zx0qEOdP6>^DNVNaT(IT9=l(wI4$(R?Ag@HH%zCrHdaQ}b%!17JXa)(sQ z&h6t(U0_Bi1*^6ivs?Z~wiy*TES1_#dNusX8{x35kBUpEVvm>r4OOW?-!9d1d8oM5 z-YWtpp$p6mZYTxaYKpyS!hzv;{I0i%4r=(}9l}uA^w5L5&BX<#w$@~J$*R4jvn!BP zL{8Dy1=aVu~dCx3pv2m ljwjXR#;WQr*Rleb`~gs*x!%&vWWly literal 0 HcmV?d00001 diff --git a/example/fonts/optixBold.png b/example/fonts/optixBold.png new file mode 100644 index 0000000000000000000000000000000000000000..cc492cb9367d7c3678cea34539ab87399674f8f4 GIT binary patch literal 1542 zcmV+h2Ko7kP)Px)z)3_wRCt{2TuGALI1D5e`u~^dgIp{SOJWhFZbulGsZe4`5?lbOJ$;PPdmnAI z(f-Ttmp;ZPQC)yEh24gjj+OYXLDOvtQXK+@H3-B43|)uP3eWtia~K3aYQ6U^{VkBx z0g(I9ikN*~GjZjP?Ury(!fG+BAJ)T5zI$Esd7<&ix-?avz!hCL?e+9ThBdi3)0{E#OWeS<&uFKps-r9JurbEvX#S8C<&T+_)a=vebVAkWH-*Jw zKI0(2xYI9Cq%Cjulqo4O+X96Y6Lfo z>vLME1sFx?bE+?C;Bj4OcE~`~>bI}r>NEZ_?g`k)@ldE4xdG5c0{8^mC|hn>-X>%X z*@}WUR$=qu0?KlAG`oz(f+?!2gFaCqoE3MCOK%p9y^R!@im{4BReHefX+@?s`WaB z21uguHRh(QL&@fYD2I0U2S*0v41Zv{~4go52cCQLVqk`WEUg4$t8!xAvmuz1_i9akro;2|}u}b;MZ^W{zhA z*QlUe0;rU~RU+<#je`?et9^pRW|Zb-RLarv6EWjOHs76|BSof(fdGchTZJt-A%&dL0w2~c$$ngdK43;V7?~AN?>+Pq0W==go&b7r+l^8qv4bxx~| zr8cN>w+ox8Aj6" + +; For demonstration purposes, both of these pieces of text are in different banks, +; and both in a bank other than the VWF engine +SECTION "Text", ROMX,BANK[3] + +Text: + db "Hello World!\n" + db "Text should break here... automatically!\n" + db "\n" + db "Text resumes printing when pressing A, but holding B works too.\n" + + db "Let's tour through most of the functionality, shall we?\n" + ; Need to split this line because of RGBDS' stupid limitations... + db "The engine is also aware of textbox height, and will replace newlines (including those inserted automatically) with commands to scroll the textbox. ","It also keeps track of how many lines have been written since the last user input, and automagically inserts pauses at the right time!" + + db "Note that automatic hyphenation is not supported, but line breaking is hyphen-aware.\n" + db "Breaking of long words can be hinted at using \"soft hyphens\". Isn't it totally amazing?" + + db "It is, ",5,"of course, ",10,"possible to insert ma",20,"nu",20,"al",20," delays, manual line\nbreaks, and, as you probably already noticed, manual button waits.\n" + + db "The engine also supports synchronisation! It's how these words",1," are made to print slowly, and back again. ",20,"It could be useful e.g. for sound cues." + + ; `SET_COLOR 0` also works, but it's pretty pointless, so I'm not showcasing it + db "It's also possible to ",1,"change ",2,"the color ",3,"of text!\n" + db "You can also switch to ",2,"variations of the font, ",$10,"a different font, or ",2,"a variation of a different font, why not!\n" + db "Each font can have up to ", NB_FONT_CHARACTERS / 100 + "0", (NB_FONT_CHARACTERS / 10) % 10 + "0", NB_FONT_CHARACTERS % 10 + "0", " characters. The encoding is left up to you--make good use of RGBASM's `charmap` feature!" + + db "The engine also supports a `call`-like mechanism. The following string is pulled from ROM2:{X:CalledText}: \"" + ; Control chars are also made available as `TEXT_*` symbols if `EXPORT_CONTROL_CHARS` is passed to the engine + db TEXT_CALL, BANK(CalledText), LOW(CalledText), HIGH(CalledText) + db "\".\n" + db "A \"jump\" is also supported." + db TEXT_JUMP, BANK(CreditsText), LOW(CreditsText), HIGH(CreditsText) + +SECTION "Credits text", ROMX,BANK[1] + +CreditsText: + db "♥ Credits ♥\n" + db "VWF engine by ISSOtm; graphics by BlitterObject; fonts by PinoBatch & Optix, with edits by ISSOtm.\n" + db "\n" + db "Text will now end, press START to begin again." + +SECTION "Static text", ROMX,BANK[2] + +StaticText: + db "VWF engine 1.0.0\n" + db "github.com/ISSOtm/gb-vwf" + + +SECTION "LCDMemcpy", ROM0 + +LCDMemcpy:: + ; Increment B if C is nonzero + dec bc + inc b + inc c +.loop + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .loop + ld a, [de] + ld [hli], a + inc de + ld a, [de] + ld [hli], a + inc de + dec c + jr nz, .loop + dec b + jr nz, .loop + ret + +LCDMemsetSmall:: + ld b, a +LCDMemsetSmallFromB:: + ldh a, [rSTAT] + and STATF_BUSY + jr nz, LCDMemsetSmallFromB + ld a, b + ld [hli], a + dec c + jr nz, LCDMemsetSmallFromB + ret + + +SECTION "Vectors", ROM0[0] + + ret + ds $08 - @ + +WaitVBlank:: + ld a, 1 + ldh [hVBlankFlag], a +.wait + halt + jr .wait + ds $10 - @ + +MemsetSmall: + ld [hli], a + dec c + jr nz, MemsetSmall + ret + ds $18 - @ + +MemcpySmall: + ld a, [de] + ld [hli], a + inc de + dec c + jr nz, MemcpySmall + ret + ds $20 - @ + + ret + ds $28 - @ + + ret + ds $30 - @ + + ret + ds $38 - @ + + ret + ds $40 - @ + + ; VBlank handler + push af + ld a, HIGH(wShadowOAM) + call hOAMDMA + + ldh a, [hVBlankFlag] + and a + jr z, .noVBlank + + ld c, LOW(rP1) + ld a, P1F_GET_DPAD + ldh [c], a + REPT 4 + ldh a, [c] + ENDR + or $F0 + ld b, a + swap b + ld a, P1F_GET_BTN + ldh [c], a + REPT 4 + ldh a, [c] + ENDR + or $F0 + xor b + ld b, a + ld a, P1F_GET_NONE + ldh [c], a + ldh a, [hHeldButtons] + cpl + and b + ldh [hPressedButtons], a + ld a, b + ldh [hHeldButtons], a + + pop af ; Pop return address to exit `waitVBlank` + xor a + ldh [hVBlankFlag], a +.noVBlank + pop af + reti + + +SECTION UNION "8800 tiles", VRAM[$8800] + +vButtonTiles: +.frame0 + ds 16 * NB_BUTTON_SPRITES +.frame1 + ds 16 * NB_BUTTON_SPRITES + +vBorderTiles: +.top + ds 16 * NB_BORDER_TOP_TILES +.vert + ds 16 * NB_BORDER_VERT_TILES +.bottom + ds 16 * NB_BORDER_BOTTOM_TILES + +vStaticTextTiles: + ds 16 * 32 +.end + + +SECTION UNION "9000 tiles", VRAM[$9000] + +vBlankTile: + ds 16 +vTextTiles:: ; Random position for demonstration purposes + ds 16 * 127 +.end + +SECTION UNION "9800 tilemap", VRAM[_SCRN0] + + ds SCRN_VX_B * 3 + + ds 1 +vTextboxTopRow: + ds TEXT_WIDTH_TILES + 2 + ds SCRN_VX_B - TEXT_WIDTH_TILES - 3 + + ds 2 +vText:: +.row0 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row1 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row2 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row3 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row4 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row5 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row6 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 2 +.row7 + ds TEXT_WIDTH_TILES + ds SCRN_VX_B - TEXT_WIDTH_TILES - 2 + + ds 1 +vTextboxBottomRow: + ds TEXT_WIDTH_TILES + 2 + ds SCRN_VX_B - TEXT_WIDTH_TILES - 3 + + ds SCRN_VX_B + + ds 3 +vStaticText: + ds SCRN_VX_B - 3 + + +SECTION "Shadow OAM", WRAM0,ALIGN[8] + +wShadowOAM: +wButtonSprites: + ds sizeof_OAM_ATTRS * NB_BUTTON_TILES / 4 + +NB_UNUSED_SPRITES equ OAM_COUNT - (@ - wShadowOAM) / sizeof_OAM_ATTRS + ds NB_UNUSED_SPRITES * sizeof_OAM_ATTRS + +SECTION "WRAM0", WRAM0 + +wBtnAnimCounter: + db + + +SECTION "HRAM", HRAM + +hCurROMBank:: + db + +hVBlankFlag: + db +hHeldButtons:: + db +hPressedButtons:: + db +hOAMDMA: + ds OAMDMA.end - OAMDMA diff --git a/example/vwf_config.asm b/example/vwf_config.asm new file mode 100644 index 0000000..c16805f --- /dev/null +++ b/example/vwf_config.asm @@ -0,0 +1,42 @@ +INCLUDE "hardware.inc/hardware.inc" + + +; The example code uses `lb`. Only it, though, so it's defined here. +lb: macro + ld \1, (\2) << 8 | (\3) +endm + + +;; The following defines are a sort of "configuration" passed to the VWF engine +;; The engine generally cares about whether they're present, not their value + + +; The engine expects to be able to read held buttons from `hHeldKeys`, and buttons just pressed from `hPressedKeys` +; `main.asm` (intentionally) defines `hHeldButtons` and `hPressedButtons` instead +; If this problem arises, here's how to work around it: +hPressedKeys equs "hPressedButtons" +hHeldKeys equs "hHeldButtons" +; The engine similarly expects to be able to read the current ROM bank from `hCurROMBank`. +; Do like above if necessary. + + +; Charset IDs increase 2 by 2 +CHARSET_0 equs "fonts/BaseSeven.vwf" +CHARSET_2 equs "fonts/BaseSevenBold_vx8.vwf" +CHARSET_16 equs "fonts/optix.vwf" +CHARSET_18 equs "fonts/optixBold.vwf" +NB_CHARSETS equ 10 + +SKIP_HELD_KEYS equ PADF_B +SKIP_PRESSED_KEYS equ PADF_A + +EXPORT_CONTROL_CHARS equ 1 + +PRINT_CHARMAP equ 1 +INCLUDE "../vwf.asm" + + +; It's possible to export symbols from `vwf.asm`, and use them elsewhere, like so +; Do so at your own risk, this isn't officially supported functionality + EXPORT _PrintVWFChar + EXPORT NB_FONT_CHARACTERS diff --git a/make_font.py b/make_font.py new file mode 100755 index 0000000..1fac009 --- /dev/null +++ b/make_font.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +from PIL import Image +from sys import argv,stderr + + +assert len(argv) > 2, "Usage:\n\t{} input_file output_file".format(argv[0]) + +CHARACTER_WIDTH = 9 +CHARACTER_HEIGHT = 8 + +with Image.open(argv[1]) as img: + bg_color = img.getpixel((0, 0)) + fg_color = img.getpixel((1, 0)) + null_color = img.getpixel((2, 0)) + + width, height = img.size + assert width % CHARACTER_WIDTH == 0, f"The source image's width must be a multiple of {CHARACTER_WIDTH}!" + assert height % CHARACTER_HEIGHT == 1, f"The source image's height must be a multiple of {CHARACTER_HEIGHT}, plus 1!" + + data = [] + + for ty in range(0, height - 1, CHARACTER_HEIGHT): + for tx in range(0, width, CHARACTER_WIDTH): + size = 0 + for y in range(ty + 1, ty + 1 + CHARACTER_HEIGHT): + byte = 0 + size = CHARACTER_WIDTH + for x in range(tx, tx + CHARACTER_WIDTH): + byte <<= 1 + pixel = img.getpixel((x, y)) + if pixel == fg_color: + byte |= 1 + elif pixel == null_color: + size -= 1 + elif pixel != bg_color: + print(f"WARNING: pixel at (x:{x}, y:{y}) is none of the 3 font pixels; treating as blank. Note: only monochrome fonts are supported!", file=stderr) + if byte & (1 << (CHARACTER_WIDTH - 8) - 1): + raise ValueError(f"Row at (x:{tx}, y:{y}): only the first 8 pixels of a character can be non-blank!") + data.append(byte >> (CHARACTER_WIDTH - 8)) + data.append(size) + +with open(argv[2], "wb") as output: + output.write(bytes(data)) diff --git a/vwf.asm b/vwf.asm index 1e34cb5..958a1c7 100644 --- a/vwf.asm +++ b/vwf.asm @@ -1,6 +1,9 @@ + +; SPDX-License-Identifier: MIT +; ; MIT License ; -; Copyright (c) 2018 Eldred Habert +; Copyright (c) 2018-2020 Eldred Habert ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal @@ -21,162 +24,370 @@ ; SOFTWARE. +; Number of elements the text stack has room for +; Having more will cause a soft crash +; This must not exceeed $7F, as the return logic discards bit 7 when checking for zero +IF !DEF(TEXT_STACK_CAPACITY) +TEXT_STACK_CAPACITY equ 8 +ENDC + +; IMPORTANT NOTE REGARDING NEWLINES!!! +; DO NOT PRINT MORE THAN THIS NEWLINES AT ONCE +; THIS **WILL** CAUSE A BUFFER OVERFLOW +IF !DEF(TEXT_NEWLINE_CAPACITY) +TEXT_NEWLINE_CAPACITY equ 10 +ENDC + + +; `wTextFlags` bits +rsset 6 +text_flag: MACRO +TEXTB_\1 rb 1 +TEXTF_\1 equ 1 << TEXTB_\1 + EXPORT TEXTB_\1, TEXTF_\1 +ENDM + text_flag WAITBUTTON + text_flag SYNC + + +CHARACTER_HEIGHT equ 8 +CHARACTER_SIZE equ CHARACTER_HEIGHT + 1 + + + SECTION "VWF engine", ROM0 + +CTRL_CHAR_PTRS equs "" + rsreset +control_char: MACRO + IF DEF(PRINT_CHARMAP) + PRINTT "charmap \"<\1>\", {d:_RS}\n" + ENDC +TEXT_\1 rb 1 + IF DEF(EXPORT_CONTROL_CHARS) + EXPORT TEXT_\1 + ENDC + + IF _NARG > 1 + dw \2 +TMP equs "{CTRL_CHAR_PTRS}\ndw \3" + PURGE CTRL_CHAR_PTRS +CTRL_CHAR_PTRS equs "{TMP}" + PURGE TMP + ENDC +ENDM + + ;;;;;;;;;;;;;;;;;;;;; "Regular" control chars ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + control_char END +RefillerControlChars: + control_char SET_FONT, ReaderSetFont, TextSetFont + control_char RESTORE_FONT, ReaderRestoreFont, TextRestoreFont + control_char SET_VARIANT, ReaderSetVariant, TextSetVariant + control_char RESTORE_VARIAN, ReaderRestoreVariant, TextRestoreVariant + control_char SET_COLOR, Reader2ByteNop, TextSetColor + control_char BLANKS, ReaderPrintBlank, TextPrintBlank + control_char DELAY, Reader2ByteNop, TextDelay + control_char WAITBTN, ReaderWaitButton, TextWaitButton + control_char CLEAR, ReaderClear, TextClear + control_char NEWLINE, ReaderNewline, TextNewline + control_char SYNC, Reader1ByteNop, TextSync + control_char SCROLL, ReaderScroll, TextScroll + control_char WAITBTN_SCROLL, ReaderWaitButtonScroll, TextWaitButtonScroll + control_char ZWS, _RefillCharBuffer.canNewline, PrintNextCharInstant +TEXT_BAD_CTRL_CHAR rb 0 + + assert TEXT_NEWLINE == "\n" + +PTRS equs "" + rsset 256 +reader_only_control_char: MACRO +_RS = _RS - 1 + IF DEF(PRINT_CHARMAP) + PRINTT "charmap \"<\1>\", {d:_RS}\n" + ENDC +TEXT_\1 equ _RS + IF DEF(EXPORT_CONTROL_CHARS) + EXPORT TEXT_\1 + ENDC + +TMP equs "dw \2\n{PTRS}" + PURGE PTRS +PTRS equs "{TMP}" + PURGE TMP +ENDM + + ;;;;;;;;;;;;;;;;;;;; Reader-only control chars ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + reader_only_control_char CALL, ReaderCall + reader_only_control_char JUMP, ReaderJumpTo + reader_only_control_char SOFT_HYPHEN, ReaderSoftHyphen + + ; The base of the table is located at its end + ; Unusual, I know, but it works better! + PTRS +RefillerOnlyControlChars: +FIRST_READER_ONLY_CONTROL_CHAR rb 0 + +NB_FONT_CHARACTERS equ FIRST_READER_ONLY_CONTROL_CHAR - " " + + + ; Sets the pen's position somewhere in memory ; The code is designed for a pen in VRAM, but it can be anywhere ; Please call this after PrintVWFText, so wTextCurTile is properly updated ; @param hl The address to print to (usually in the tilemap) SetPenPosition:: ; Note: relied upon preserving HL - ld a, l - ld [wPenPosition], a - ld [wPenStartingPosition], a - ld a, h - ld [wPenPosition + 1], a - ld [wPenStartingPosition + 1], a + ld a, l + ld [wPenPosition], a + ld [wPenStartingPosition], a + ld a, h + ld [wPenPosition + 1], a + ld [wPenStartingPosition + 1], a - ld a, [wTextCurTile] - ld [wPenCurTile], a - ret + ld a, [wTextCurTile] + ld [wPenCurTile], a + ret DrawVWFChars:: - xor a - ldh [rVBK], a - - ld hl, wPenPosition - ld a, [hli] - ld e, a - ld a, [hli] - ld d, a - ; ld hl, wPenCurTile - ld c, [hl] - - ld hl, wNewlineTiles - ld a, [wFlushedTiles] - and a - jr z, .noNewTiles - ld b, a - xor a - ld [wFlushedTiles], a + ld hl, wPenPosition + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + assert wPenPosition + 2 == wPenCurTile + ld c, [hl] + + ld hl, wNewlineTiles + ld a, [wFlushedTiles] + and a + jr z, .noNewTiles + ld b, a + xor a + ld [wFlushedTiles], a .writeNewTile - ; Check if current tile is subject to a newline - ld a, c - cp [hl] - jr z, .newline - wait_vram - ld a, c - ld [de], a - sub $7F - jr nz, .nowrap - ld c, a ; ld c, 0 + ; Check if current tile is subject to a newline + ld a, [wNbNewlines] + and a + jr z, .writeTile + ld a, c + cp [hl] + jr z, .newline +.writeTile + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .writeTile + ld a, c + ld [de], a + ld a, [wLastTextTile] + scf + sbc c + jr nc, .nowrap + ld a, [wWrapTileID] + ld c, a + db $FE ; cp imm8 .nowrap - inc de - inc c - dec b - jr nz, .writeNewTile + inc c + inc de + dec b + jr nz, .writeNewTile .noNewTiles .tryAgain - ld a, c - cp [hl] - jr z, .finalNewline - xor a - ld [wNbNewlines], a - - ld hl, wPenCurTile - ld a, c - ld [hld], a - ld a, d - ld [hld], a - ld [hl], e - - ; If the current tile is empty (1 px == 1 space) - ld a, [wTextCurPixel] - cp 2 - ret c - wait_vram - ld a, c - ld [de], a - ret + ld a, [wNbNewlines] + and a + jr z, .noFinalNewline + ld a, c + cp [hl] + jr z, .finalNewline +.noFinalNewline + xor a + ld [wNbNewlines], a + + ld hl, wPenCurTile + ld a, c + ld [hld], a + assert wPenCurTile - 1 == wPenPosition + 1 + ld a, d + ld [hld], a + ld [hl], e + + ; If the current tile is empty (1 px == 1 space) + ld a, [wTextCurPixel] + cp 2 + ret c +.waitFinalTile + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .waitFinalTile + ld a, c + ld [de], a + ret .newline - ld a, [wPenStartingPosition] - and SCRN_VX_B - 1 - ld c, a ; Get offset from column 0 - ld a, e - and -SCRN_VX_B ; Get to column 0 - add a, c ; Get to starting column - add a, SCRN_VX_B ; Go to next row (this might overflow) - ld e, a - jr nc, .nocarry - inc d + ld a, [wNbNewlines] + dec a + ld [wNbNewlines], a + ld a, [wPenStartingPosition] + and SCRN_VX_B - 1 + ld c, a ; Get offset from column 0 + ld a, e + and -SCRN_VX_B ; Get to column 0 + add a, c ; Get to starting column + add a, SCRN_VX_B ; Go to next row (this might overflow) + ld e, a + jr nc, .nocarry + inc d .nocarry - ld c, [hl] ; Get back tile ID - xor a ; Clear this newline tile - ld [hli], a ; Go to the next newline (if any) - jr .writeNewTile + ld c, [hl] ; Get back tile ID + xor a ; Clear this newline tile + ld [hli], a ; Go to the next newline (if any) + jr .writeNewTile .finalNewline - xor a - ld [hli], a ; Clear that - ld a, [wPenStartingPosition] - and SCRN_VX_B - 1 - ld b, a - ld a, e - and -SCRN_VX_B - add a, b - add a, SCRN_VX_B - ld e, a - jr nc, .tryAgain ; noCarry - inc d - jr .tryAgain - - - + ld a, [wNbNewlines] + dec a + ld [wNbNewlines], a + xor a + ld [hli], a ; Clear that + ld a, [wPenStartingPosition] + and SCRN_VX_B - 1 + ld b, a + ld a, e + and -SCRN_VX_B + add a, b + add a, SCRN_VX_B + ld e, a + jr nc, .tryAgain ; noCarry + inc d + jr .tryAgain + + + +TEXT_CONT_STR equ 0 +TEXT_NEW_STR equ 1 + EXPORT TEXT_CONT_STR + EXPORT TEXT_NEW_STR ; Sets up the VWF engine to start printing text +; WARNING: If flushing the string, the auto-wordwrapper assumes that a new line is started +; (You might get odd gfx otherwise if the text ended close enough to the tile border) +; If needed, manually modify `wLineRemainingPixels` after calling this +; WARNING: If you want to print to a specific tile ID, set wTextCurTile *after* calling this +; (Obviously, don't do this if you're not flushing the string) +; WARNING: Variant is reset when calling this, but the language is preserved +; WARNING: Source data located between $FF00 and $FFFF will probably cause some malfunctioning +; NOTICE: Source data outside of ROM banks is fine, but banking won't be performed. +; Any ROM bank number is thus fine, but avoid 0 or values too high as this triggers spurious warnings in BGB ; @param hl Pointer to the string to be displayed ; @param b Bank containing the string ; @param a Non-zero to flush the current string (use zero if you want to keep printing the same string) +; @return a 0 +; @return hl wTextCharset +; @return f NC and Z +; @destroy bc de PrintVWFText:: - and a ; Set Z flag for test below - - ; Write src ptr - ld a, b - ld [wTextSrcBank], a - ld a, l - ld [wTextSrcPtr], a - ld a, h - ld [wTextSrcPtr + 1], a - - ; Flag preserved from `and a` - jr z, .dontFlush - xor a - ldh [rVBK], a - ; Don't flush if current tile is empty - ld a, [wTextCurPixel] - cp 2 - ; Flush buffer to VRAM - call nc, FlushVWFBuffer - ; Reset position always, though - xor a - ld [wTextCurPixel], a + and a ; Set Z flag for test below + + ; Write src ptr + ld a, b + ld [wTextSrcBank], a + ld a, l + ld [wTextSrcPtr], a + ld a, h + ld [wTextSrcPtr + 1], a + + ; Flag preserved from `and a` + jr z, .dontFlush + ; Reset auto line-wrapper (assuming we're starting a new line) + ld a, [wTextLineLength] + ld [wLineRemainingPixels], a + ; Don't flush if current tile is empty + ld a, [wTextCurPixel] + cp 2 ; Again, last pixel of all tiles is empty + call nc, FlushVWFBuffer + ; Reset position always, though + xor a + ld [wTextCurPixel], a + ld [wNbNewlines], a .dontFlush - ; Use black color by default (which is normally loaded into color #3) - ld a, 3 - ld [wTextColorID], a + ; Force buffer refill by making these two identical + ; wTextReadPtrLow needs to be this to refill the full buffer + ld a, LOW(wTextCharBufferEnd) + ld [wTextFillPtrEnd], a + ld [wTextReadPtrEnd], a + ld [wTextReadPtrLow], a + + ; Use black color by default (which is normally loaded into color #3) + ld a, 3 + ld [wTextColorID], a - ; Preserve the language but reset the decoration - ld hl, wTextCharset - ld a, [hl] - and $F0 - ld [hl], a + ; Preserve the language but reset the decoration + ld hl, wTextCharset + ld a, [hl] + and $F0 + ld [hl], a - ; Set initial letter delay to 0, to start printing directly - xor a - ld [wTextNextLetterDelay], a - ret + ; Set initial letter delay to 0, to start printing directly + xor a + ld [wTextNextLetterDelay], a + ret + +FlushVWFBuffer:: + push hl + + ; Calculate ptr to next tile + ld a, [wTextTileBlock] + ld h, a + ld a, [wTextCurTile] + swap a ; Multiply by 16 + ld d, a + and $F0 + ld e, a + xor d + add a, h + ld d, a + + ; Copy buffer 1 to VRAM, buffer 2 to buffer 1, and clear buffer 2 + ld hl, wTextTileBuffer + ld bc, wTextTileBuffer + $10 +.copyByte + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .copyByte + ; Write tile buf to VRAM + ld a, [hl] + ld [de], a + inc e ; Faster than inc de, guaranteed thanks to ALIGN[4] + ; Copy second tile to first one + ld a, [bc] + ld [hli], a + ; Clear second tile + xor a + ld [bc], a + inc c + + ld a, l + cp LOW(wTextTileBuffer + $10) + jr nz, .copyByte + + ; Go to next tile + ld hl, wLastTextTile + ld a, [hld] + ld c, a + assert wLastTextTile - 1 == wTextCurTile + ld a, [hl] + cp c + inc a + jr c, .noWrap + ld a, [wWrapTileID] +.noWrap + ld [hl], a + + ld hl, wFlushedTiles + inc [hl] + pop hl + ret ; Prints a VWF char (or more), applying delay if necessary ; Might print more than 1 char, eg. if wTextLetterDelay is zero @@ -184,748 +395,1088 @@ PrintVWFText:: ; **DO NOT CALL WITH SOURCE DATA IN FF00-FFFF, THIS WILL CAUSE AN EARLY RETURN!! ; Number of tiles to write to the tilemap is written in wFlushedTiles PrintVWFChar:: - ld hl, wTextNextLetterDelay - ld a, [hl] - and a - jr z, .delayFinished - dec a - ld [hl], a - ret - -.delayFinished - ; xor a - ld [wFlushedTiles], a - ldh [rVBK], a - - ; Save current ROM bank - ldh a, [hCurROMBank] - push af - - ; Get read to read char - ld hl, wTextSrcBank - ld a, [hli] - rst bankswitch - ld a, [hli] - ld h, [hl] - ld l, a + ld hl, wTextNextLetterDelay + ld a, [hl] + and a + jr nz, .delay + ; xor a + ld [wFlushedTiles], a + + ldh a, [hCurROMBank] + push af + ld a, BANK(_PrintVWFChar) + ldh [hCurROMBank], a + ld [rROMB0], a + call _PrintVWFChar + pop af + ldh [hCurROMBank], a + ld [rROMB0], a + ret + +.delay + dec a + ld [hl], a + ret + + +RefillerOnlyControlChar: + ld bc, _RefillCharBuffer + push bc + + push hl + add a, a + add a, LOW(RefillerOnlyControlChars) + ld l, a + ld a, $FF ; If we're here, the value in A is negative + adc a, HIGH(RefillerOnlyControlChars) + jr RefillerJumpControlChar + +RefillerControlChar: + add a, " " ; Restore the original value + add a, a ; Double for pointer calculation, and check for 0 + jr z, RefillerTryReturning + + ld bc, _RefillCharBuffer.afterControlChar + push bc + inc e ; Otherwise the char isn't counted to be written! + push hl + add a, LOW(RefillerControlChars - 2) + ld l, a + adc a, HIGH(RefillerControlChars - 2) + sub l +RefillerJumpControlChar: + ld h, a + ld a, [hli] + ld b, [hl] + ld c, a + pop hl + push bc + ret + + +RefillerTryReturning: + ld hl, wTextStackSize + ld a, [hl] + ld b, a + add a, a + ld [de], a ; If we're returning, we will need to write that $00; otherwise, it'll be overwritten + jp z, _RefillCharBuffer.done ; Too far to `jr` + dec b + ld [hl], b + add a, b ; a = stack size * 3 + 2 + add a, LOW(wTextStack) + ld l, a + adc a, HIGH(wTextStack) + sub l + ld h, a + jr RestartCharBufRefill + +; Refills the char buffer, assuming at least half of it has been read +; Newlines are injected into the buffer to implement auto line-wrapping +; @param hl The current read ptr into the buffer +RefillCharBuffer: + ld de, wTextCharBuffer + ; First, copy remaining chars into the buffer + ld a, [wTextFillPtrEnd] + sub l + ld c, a + jr z, .charBufferEmpty +.copyLeftovers + ld a, [hli] + ld [de], a + inc e + dec c + jr nz, .copyLeftovers +.charBufferEmpty + + ; Cache charset ptr to speed up calculations + ld a, [wTextCharset] + ld [wRefillerCharset], a + add a, LOW(CharsetPtrs) + ld l, a + adc a, HIGH(CharsetPtrs) + sub l + ld h, a + ld a, [hli] + add a, 8 ; Code later on will want a +8 offset + ld [wCurCharsetPtr], a + ld a, 0 ; If you try to optimize this to `xor a` I will kick you in the nuts + adc a, [hl] + ld [wCurCharsetPtr+1], a + + ; Set a position to insert a newline at if the first word overflows + xor a + ld [wNewlinePtrLow], a + + ; Get ready to read chars into the buffer + ld hl, wTextSrcBank +RestartCharBufRefill: + ld a, [hld] + ldh [hCurROMBank], a + ld [rROMB0], a + assert wTextSrcBank - 1 == wTextSrcPtr + 1 + ld a, [hld] + ld l, [hl] + ld h, a + +_RefillCharBuffer: + ld a, [hli] + cp FIRST_READER_ONLY_CONTROL_CHAR + jr nc, RefillerOnlyControlChar + ld [de], a + sub " " + jr c, RefillerControlChar ; The refiller needs to be aware of some control chars + + ; Add char length to accumulated one + push hl + ld hl, wCurCharsetPtr + ld c, [hl] + inc hl + ld b, [hl] + ld l, a + ld h, 0 + add hl, hl + add hl, hl + add hl, hl + add hl, bc ; Char * 8 + base + 8 + ld c, a ; Stash this for later + ld b, 0 + add hl, bc ; Char * 9 + base + 8 + ld a, [wLineRemainingPixels] + sub a, [hl] +.insertCustomSize + jr nc, .noNewline + ld b, a ; Stash this for later + ; Line length was overflowed, inject newline into buffer + ; Get ptr to newline injection point + ld h, d ; ld h, HIGH(wTextCharBuffer) + ld a, [wNewlinePtrLow] + ld l, a + ld d, "\n" + ld a, [wTextRemainingLines] + dec a + jr nz, .linesRemain + inc a + ld d, TEXT_SCROLL +.linesRemain + ld [wTextRemainingLines], a + ld a, [wNewlinesUntilFull] + dec a + jr nz, .dontPause + ld d, TEXT_WAITBTN_SCROLL + ld a, [wTextNbLines] +.dontPause + ld [wNewlinesUntilFull], a + ; Dashes aren't overwritten on newlines, instead the newline is inserted right after + ld a, [hl] + cp " " + jr z, .overwritingNewline + cp TEXT_ZWS + jr nz, .noSoftHyphen + ; A soft hyphen causes a hyphen to be placed over it, after which the newline is inserted + ld a, "-" + ld [hli], a +.noSoftHyphen + ; We're going to shift the entire buffer right, so count an extra char... + ; ...unless doing so would overflow the buffer. + ld a, e + cp LOW(wTextCharBufferEnd - 1) + jr z, .bufferFull + inc e +.bufferFull + ; Swap characters between `d` and `[hl]` (we already have the char to be inserted in `d`) +.copyNewlinedChars + ld a, d + ld d, [hl] + ld [hli], a + ld a, e ; Stop when we're about to write the last char + cp l + jr nz, .copyNewlinedChars + ; But write it, of course! +.overwritingNewline + ld [hl], d + ; Restore dest ptr high byte + ld d, h ; ld d, HIGH(wTextCharBuffer) + ; Compute the amount of pixels remaining after inserting the newline + ; pixels now = pixels before word - word length ⇒ word length = pixels before word - pixels now + ld a, [wPixelsRemainingAtNewline] + sub b + ld b, a + ; new line's pixels = line length - word length + ld a, [wTextLineLength] + sub b +.noNewline + ld [wLineRemainingPixels], a + pop hl + + ld a, c + ; If the character is a dash or a space, a newline can be inserted + and a ; cp " " - " " + jr z, .canNewline + inc e ; This increment is also shared by the main loop + cp "-" - " " ; Dashes aren't *overwritten* by the newline, instead it's inserted after + ld a, e ; The increment has to be placed in an awkward way because it alters flags + jr z, .canNewlineAfter + +.afterControlChar + ld a, e + cp LOW(wTextCharBufferEnd - 2) ; Give ourselves some margin due to multi-byte control chars + jr c, _RefillCharBuffer + + dec e ; Compensate for what's below +.done + inc e ; If we jumped to .done, we have written a terminator, account for it + ; Write src ptr for later + ld a, l + ld [wTextSrcPtr], a + ld a, h + ld [wTextSrcPtr+1], a + ldh a, [hCurROMBank] + ld [wTextSrcBank], a + ; End the buffer refill at newlineable chars only + ld a, [wNewlinePtrLow] + cp 2 + jr nc, .foundNewlineableChar + ld a, e +.foundNewlineableChar + ld [wTextReadPtrEnd], a + ld a, e + ld [wTextFillPtrEnd], a + + ld a, BANK(_PrintVWFChar) + ldh [hCurROMBank], a + ld [rROMB0], a + ; Restart printer's reading + ld hl, wTextCharBuffer + ret + + +.canNewline + ld a, e + inc e +.canNewlineAfter + ld [wNewlinePtrLow], a + ld a, [wLineRemainingPixels] + ld [wPixelsRemainingAtNewline], a + jr .afterControlChar + + +Reader2ByteNop: + ld a, [hli] + ld [de], a + inc e +Reader1ByteNop: + ret + +ReaderSoftHyphen: + ; TODO: the added hyphen might overflow the line when wrapping occurs + pop bc ; We will take a detour instead of returning + ld a, TEXT_ZWS + ld [de], a + jr _RefillCharBuffer.canNewline + +ReaderSetFont: + ld a, [wRefillerCharset] + ld c, a + ld [wRefillerPrevFont], a + ld a, [hli] + ld [de], a + inc e + xor c + and $F0 + jr ReaderUpdateCharset + +ReaderRestoreFont: + ld a, [wRefillerCharset] + and $0F + ld c, a + ld a, [wRefillerPrevFont] + and $F0 + jr ReaderUpdateCharset + +ReaderSetVariant: + ld a, [wRefillerCharset] + ld c, a + ld [wRefillerPrevVariant], a + ld a, [hli] + ld [de], a + inc e + xor c + and $0F + jr ReaderUpdateCharset + +ReaderRestoreVariant: + ld a, [wRefillerCharset] + and $F0 + ld c, a + ld a, [wRefillerPrevVariant] + and $0F + ; Fall through +ReaderUpdateCharset: + xor c + ld [wRefillerCharset], a + add a, LOW(CharsetPtrs) + ld c, a + adc a, HIGH(CharsetPtrs) + sub c + ld b, a + ld a, [bc] + add a, 8 ; Add the offset to the character widths + ld [wCurCharsetPtr], a + inc bc + ld a, [bc] + adc a, 0 + ld [wCurCharsetPtr+1], a + ret + +ReaderPrintBlank: + pop bc ; We're not gonna return because we're gonna insert a custom size instead + ld a, [hli] ; Read number of blanks + ld [de], a + ; inc e ; Don't increment dest ptr because the code path we'll jump into will do it + ld c, a + ld a, [wLineRemainingPixels] + sub c + ; We'll be jumping straight in the middle of some code path, make sure not to break it + push hl + ld c, "A" ; Make sure we won't get a newline + jp _RefillCharBuffer.insertCustomSize ; Too far to `jr` + +ReaderWaitButton: + ; Don't auto-wait for user input until the textbox has been entirely freshened + ld a, [wTextNbLines] + inc a ; The next newline will actually start introducing new text + ld [wNewlinesUntilFull], a + ret + + ; For the purpose of line length counting, newline, clearing and scrolling are the same + ; For height counting, however... +ReaderNewline: + ld a, [wTextRemainingLines] + dec a + ld [wTextRemainingLines], a + jr nz, ReaderScroll.checkFullBox + dec e ; dec de + ld a, TEXT_SCROLL + ld [de], a + inc e ; inc de + +ReaderScroll: + ld a, [wTextRemainingLines] + inc a + ld [wTextRemainingLines], a +.checkFullBox + ld a, [wNewlinesUntilFull] + dec a + jr nz, StartNewLine + dec e ; dec de + ld a, TEXT_WAITBTN_SCROLL + ld [de], a + inc e ; inc de +ReaderWaitButtonScroll: + ld a, [wTextRemainingLines] + inc a + ld [wTextRemainingLines], a + ld a, [wTextNbLines] + jr StartNewLine + +ReaderClear: + ld a, [wTextNbLines] + ld [wTextRemainingLines], a +StartNewLine: + ld [wNewlinesUntilFull], a + ; Reset line length, since we're forcing a newline + ld a, [wTextLineLength] + ld [wLineRemainingPixels], a + ret + +; Sets text ptr to given location +ReaderJumpTo: + ld a, [hli] + ld b, a + ld a, [hli] + ld h, [hl] + ld l, a + ld a, b + ldh [hCurROMBank], a + ld [rROMB0], a + ret + +; Start printing a new string, then keep writing this one +; NOTE: avoids corruption by preventing too much recursion, but this shouldn't happen at all +ReaderCall: + ld a, [wTextStackSize] + IF DEF(STACK_OVERFLOW_HANDLER) + cp TEXT_STACK_CAPACITY + call nc, STACK_OVERFLOW_HANDLER + ENDC + + ; Read target ptr + inc a ; Increase stack size + ld [wTextStackSize], a + + ; Get ptr to end of 1st empty entry + ld b, a + add a, a + add a, b + add a, LOW(wTextStack - 1) + ld c, a + adc a, HIGH(wTextStack - 1) + sub c + ld b, a + ; Save ROM bank immediately, as we're gonna bankswitch + ldh a, [hCurROMBank] + ld [bc], a + dec bc + + ; Swap src ptrs + ld a, [hli] + ld [de], a ; Use current byte in char buffer as scratch + ; Save src ptr now + inc hl + inc hl + ld a, h + ld [bc], a + dec bc + ld a, l + ld [bc], a + ; Read new src ptr + dec hl + ld a, [hld] + ld l, [hl] + ld h, a + ; Perform bankswitch now that all bytecode has been read + ld a, [de] + ldh [hCurROMBank], a + ld [rROMB0], a + ret + + +SECTION "VWF ROMX functions + data", ROMX + +PrintVWFControlChar: + IF DEF(BAD_CTRL_CHAR_HANDLER) + ; Check if ctrl char is valid + cp TEXT_BAD_CTRL_CHAR + call nc, BAD_CTRL_CHAR_HANDLER + ENDC + + ; Control char, run the associated function + ld de, _PrintVWFChar.charPrinted + push de + + ; Push the func's addr (so we can preserve hl when calling) + add a, a + add a, LOW(ControlCharFuncs - 2) + ld e, a + adc a, HIGH(ControlCharFuncs - 2) + sub e + ld d, a + ld a, [de] + ld c, a + inc de + ld a, [de] + ld b, a + push bc + ret ; Actually jump to the function, passing `hl` as a parameter for it to read (and advance) + +_PrintVWFChar: + ld h, HIGH(wTextCharBuffer) + ld a, [wTextReadPtrLow] + ld l, a .setDelayAndNextChar - ; Reload delay - ld a, [wTextLetterDelay] - ld [wTextNextLetterDelay], a + ; Reload delay + ld a, [wTextLetterDelay] + ld [wTextNextLetterDelay], a .nextChar - ; Read byte from string stream - ld a, [hli] - and a ; Check for terminator - jp z, .checkForReturn - cp " " - jp c, .controlChar + ; First, check if the buffer is sufficiently full + ; Making the buffer wrap would be costly, so we're keeping a safety margin + ; Especially since control codes are multi-byte + ld a, [wTextReadPtrEnd] + cp l + IF DEF(OVERREAD_HANDLER) + call c, OVERREAD_HANDLER ; This needs to be first as it's a no-return + ENDC + call z, RefillCharBuffer ; If it was second this function could destroy carry and trigger it + + ; Read byte from string stream + ld a, [hli] + and a ; Check for terminator + jr z, .return + cp " " + jr c, PrintVWFControlChar ; Print char - ; Save src ptr & letter ID - push hl - - sub " " - add a, a - ld c, a - ld b, 0 - - ; Get ptr to charset table - ld a, [wTextCharset] - add a, a - add a, LOW(CharsetPtrs) - ld l, a - adc a, HIGH(CharsetPtrs) - sub l - ld h, a - ld a, [hli] - ld h, [hl] - ld l, a - - ; Get ptr to letter - add hl, bc - ld a, [hli] - ld d, [hl] - ld e, a - - ; Get dest buffer ptr - ld hl, wTextTileBuffer - - ld c, 8 + ; Save src ptr & letter ID + push hl + + sub " " + ld e, a + + ; Get ptr to charset table + ld a, [wTextCharset] + add a, LOW(CharsetPtrs) + ld l, a + adc a, HIGH(CharsetPtrs) + sub l + ld h, a + ld a, [hli] + ld b, [hl] + ld c, a + + ; Get ptr to letter + ld d, 0 + ld l, e + ld h, d ; ld h, 0 + add hl, hl + add hl, hl + add hl, hl + add hl, de ; * 9 + add hl, bc + ld d, h + ld e, l + + ; Get dest buffer ptr + ld hl, wTextTileBuffer + + ld a, 8 .printOneLine - ld a, [wTextCurPixel] - ld b, a - and a + ldh [hVWFRowCount], a + + ld a, [wTextCurPixel] + ld b, a + and a ; Check now if shifting needs to happen - ld a, [de] - inc de - push de + ld a, [de] + inc de - ld e, 0 - jr z, .doneShifting + ld c, 0 + jr z, .doneShifting .shiftRight - rra ; Actually `srl a`, since a 0 bit is always shifted in - rr e - dec b - jr nz, .shiftRight + rra ; Actually `srl a`, since a 0 bit is always shifted in + rr c + dec b + jr nz, .shiftRight .doneShifting - ld d, a - - ld a, [wTextColorID] - rra - jr nc, .noLSB - ld a, d - or [hl] - ld [hl], a - - ld a, l - add a, $10 - ld l, a - ld a, e - or [hl] - ld [hl], a - ld a, l - sub $10 - ld l, a + ld b, a + + ld a, [wTextColorID] + rra + jr nc, .noLSB + ld a, b + or [hl] + ld [hl], a + + set 4, l + ld a, c + or [hl] + ld [hl], a + res 4, l + + ld a, [wTextColorID] + rra .noLSB - inc hl - - ld a, [wTextColorID] - and 2 - jr z, .noMSB - ld a, d - or [hl] - ld [hl], a - - ld a, l - add a, $10 - ld l, a - ld a, e - or [hl] - ld [hl], a - ld a, l - sub $10 - ld l, a + inc l + + rra + jr nc, .noMSB + ld a, b + or [hl] + ld [hl], a + + set 4, l + ld a, c + or [hl] + ld [hl], a + res 4, l .noMSB - inc hl + inc l - pop de - dec c - jr nz, .printOneLine + ldh a, [hVWFRowCount] + dec a + jr nz, .printOneLine - ; Advance read by size - ld hl, wTextCurPixel - ld a, [de] - add a, [hl] - ld [hl], a + ; Advance read by size + ld hl, wTextCurPixel + ld a, [de] + add a, [hl] + ld [hl], a - ; Restore src ptr - pop hl + ; Restore src ptr + pop hl .charPrinted - ; Save src ptr - ld a, l - ld [wTextSrcPtr], a - ld a, h - ld [wTextSrcPtr + 1], a - - ; Check if flushing needs to be done - ld a, [wTextCurPixel] - sub 8 - jr c, .noTilesToFlush - - ; Move back by 8 pixels - ld [wTextCurPixel], a - ; Flush them to VRAM - call FlushVWFBuffer - ; Check if the second tile needs to be flushed as well (happens with characters 9 pixels wide) - ; We might never use 9-px chars, but if we do, there'll be support for them ^^ - ld a, [wTextCurPixel] - sub 8 - jr c, .flushed - ld [wTextCurPixel], a - call FlushVWFBuffer -.flushed + ; Check if flushing needs to be done + ld a, [wTextCurPixel] + sub 8 + jr c, .noTilesToFlush + + ; Move back by 8 pixels + ld [wTextCurPixel], a + ; Flush them to VRAM + call FlushVWFBuffer + ; Try flushing again (happens with characters 9 pixels wide) + ; We might never use 9-px chars, but if we do, there'll be support for them ^^ + jr .charPrinted + +; This block of code is only here to avoid turning a `jp` into a `jr` +.return + ; Tell caller we're done (if we're not, this'll be overwritten) + ld a, $FF + ld [wTextSrcPtr + 1], a + jr .flushAndFinish .noTilesToFlush - ; If not printing next char immediately, force to flush - ld a, [wTextNextLetterDelay] - and a - jp z, .setDelayAndNextChar - dec a - ld [wTextNextLetterDelay], a + ; If not printing next char immediately, force to flush + ld a, [wTextNextLetterDelay] + and a + jp z, .setDelayAndNextChar + dec a + ld [wTextNextLetterDelay], a + ; Write back new read ptr into buffer for next iteration + ld a, l + ld [wTextReadPtrLow], a .flushAndFinish - ; Check if flushing is necessary - ld a, [wTextCurPixel] - cp 2 - jr c, .flushingNotNeeded - - ld a, [wTextCurTile] - swap a - ld h, a - and $F0 - ld l, a - ld a, h - and $0F - add a, HIGH(vVWFTiles) - ld h, a - ld de, wTextTileBuffer - ld c, $10 - call LCDMemcpySmall - -.flushingNotNeeded - ; Restore ROM bank - pop af - rst bankswitch - ret - - -.checkForReturn - ; Tell caller we're done (if we're not, this'll be overwritten) - ld a, $FF - ld [wTextSrcPtr + 1], a - - ; Check if stack is empty - ld hl, wTextStackSize ; Ok to trash hl, it doesn't matter anyways - ld a, [hl] - add a, a ; Assuming this can't be > $7F (shouldn't be > 8 anyways) - jr z, .flushAndFinish - - add a, [hl] ; *3 - dec [hl] ; Decrement entry count - add a, LOW(wTextStack) - ld l, a - adc a, HIGH(wTextStack) - sub l - ld h, a - ; hl points to first byte of free entry, so decrement - dec hl - - ; Restore ROM bank - ld a, [hld] - rst bankswitch - - ; Read new src ptr - ld a, [hld] - ld l, [hl] - ld h, a - jp .nextChar - - -.controlChar - ; Check if ctrl char is valid - cp TEXT_BAD_CTRL_CHAR - call nc, TextCtrlCharError - - ; Control char, run the associated function - ld de, .charPrinted - push de - - ; Push the func's addr (so we can preserve hl when calling) - add a, a - add a, LOW(.controlCharFuncs - 2) - ld e, a - adc a, HIGH(.controlCharFuncs - 2) - sub e - ld d, a - ld a, [de] - ld c, a - inc de - ld a, [de] - ld b, a - push bc - ret ; Actually jump to the function, passing `hl` as a parameter for it to read (and advance) - - -.controlCharFuncs - dw TextSetLanguage - dw TextRestoreLanguage - dw TextSetDecoration - dw TextRestoreDecoration - dw TextSetColor - dw TextPrintBlank - dw TextJumpTo - dw TextCall - dw TextDelay - dw TextNewline - - -TextSetLanguage: - ld de, wTextCharset - ld a, [de] - ld b, a - ld [wPreviousLanguage], a - ld a, b - and $0F - ld b, a - ld a, [hli] - swap a - and $F0 - or b - jr _TextSetCharset - -TextRestoreLanguage: - ld de, wTextCharset - ld a, [de] - and $0F - ld b, a - ld a, [wPreviousLanguage] - and $F0 - or b - jr _TextSetCharset - -TextSetDecoration: - ld de, wTextCharset - ld a, [de] - ld b, a - ld [wPreviousDecoration], a - ld a, b - and $F0 - ld b, a - ld a, [hli] - and $0F - or b - jr _TextSetCharset - -TextRestoreDecoration: - ld de, wTextCharset - ld a, [de] - and $F0 - ld b, a - ld a, [wPreviousDecoration] - and $0F - or b + ; Check if flushing is necessary + ld a, [wTextCurPixel] + cp 2 + ret c + + ; We're not using FlushVWFBuffer because we don't want to advance the tile + ld a, [wTextTileBlock] + ld d, a + ld a, [wTextCurTile] + swap a + ld h, a + and $F0 + ld l, a + xor h + add a, d + ld h, a + ld de, wTextTileBuffer +.copyTile + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .copyTile + ld a, [de] + ld [hli], a + inc e ; inc de + ld a, [de] + ld [hli], a + inc e ; inc de + bit 4, e + jr z, .copyTile + ret + + +ControlCharFuncs: + CTRL_CHAR_PTRS + +TextDelay: + ld a, [hli] + ld [wTextNextLetterDelay], a + ret + + +TextRestoreFont: + ld de, wTextCharset + ld a, [de] + and $0F + ld b, a + ld a, [wPreviousFont] + and $F0 + jr _TextSetCharset + +TextRestoreVariant: + ld de, wTextCharset + ld a, [de] + and $F0 + ld b, a + ld a, [wPreviousVariant] + and $0F + jr _TextSetCharset + +TextSetVariant: + ld de, wTextCharset + ld a, [de] + ld [wPreviousVariant], a + and $F0 + ld b, a + ld a, [hli] + and $0F + jr _TextSetCharset + +TextSetFont: + ld de, wTextCharset + ld a, [de] + ld [wPreviousFont], a + and $0F + ld b, a + ld a, [hli] + and $F0 _TextSetCharset: - ld [de], a - jr PrintNextCharInstant + or b + ld [de], a + jr PrintNextCharInstant TextSetColor: - ld a, [hli] - and 3 - ld [wTextColorID], a - jr PrintNextCharInstant + ld a, [hli] + and 3 + ld [wTextColorID], a + jr PrintNextCharInstant + + +skip_key: MACRO + IF \1 == 1 + rra + jr c, \2 + ELIF \1 == 1 << 7 + add a, a + jr c, \2 + ELSE + and \1 + jr nz, \2 + ENDC +ENDM +TextWaitButton: + xor a ; FIXME: if other bits than 7 and 6 get used, this is gonna be problematic + ld [wTextFlags], a + ; End this char if suitable user input is found + ldh a, [hHeldKeys] + skip_key SKIP_HELD_KEYS, PrintNextCharInstant + ldh a, [hPressedKeys] + skip_key SKIP_PRESSED_KEYS, PrintNextCharInstant + ; If no button has been pressed, keep reading this char + ; Ensure the engine reacts on the very next frame to avoid swallowing buttons + ld a, 1 + ld [wTextNextLetterDelay], a + ; We know that text is running, so it's fine to overwrite bit 7 + ld a, $40 + ld [wTextFlags], a + ; Decrement src ptr so this char keeps getting read + dec hl + ret TextPrintBlank: - ld a, [hli] - ld c, a - ld a, [wTextCurPixel] - add a, c - ld c, a - and $F8 - jr z, .noNewTiles - rrca - rrca - rrca - ld b, a + ld a, [hli] + ld c, a + ld a, [wTextCurPixel] + add a, c + ld c, a + and $F8 + jr z, .noNewTiles + rrca + rrca + rrca + ld b, a .printNewTile - push bc - call FlushVWFBuffer ; Preserves HL - pop bc - dec b - jr nz, .printNewTile + push bc + call FlushVWFBuffer ; Preserves HL + pop bc + dec b + jr nz, .printNewTile .noNewTiles - ld a, c - and 7 - ld [wTextCurPixel], a - jr PrintNextCharInstant - - -TextDelay: - ld a, [hli] - ld [wTextNextLetterDelay], a - ret - - -; Sets text ptr to given location (must be within same bank!) -TextJumpTo: - ld a, [hli] - rst bankswitch - ld a, [hli] - ld h, [hl] - ld l, a - jr PrintNextCharInstant - -; Start printing a new string, then keep writing this one -; NOTE: avoids corruption by preventing too much recursion, but this shouldn't happen at all -TextCall: - ld a, [wTextStackSize] - cp TEXT_STACK_CAPACITY - call nc, TextStackOverflowError - - ; Read target ptr - ld b, a ; Save current size for later (to get ptr to 1st free entry) - inc a ; Increase stack size - ld [wTextStackSize], a - - ; Get target ptr - ld a, [hli] - rst bankswitch - ld a, [hli] - ld e, a - ld a, [hli] - ld d, a - - ; Get ptr to stack top (1st empty entry) - ld a, b - add a, a - add a, b - add a, LOW(wTextStack) - ld c, a - adc a, HIGH(wTextStack) - sub c - ld b, a - ld a, l - ld [bc], a - inc bc - ld a, h - ld [bc], a - inc bc - ldh a, [hCurROMBank] - ld [bc], a - - ; Get new src ptr - ld h, d - ld l, e - jr PrintNextCharInstant - - -TextNewline: - ; Flush the current tile if non-blank - ld a, [wTextCurPixel] - cp 2 - call nc, FlushVWFBuffer - ; Reset position - xor a - ld [wTextCurPixel], a - - ld de, wNbNewlines - ld a, [de] - inc a - ld [de], a - dec a - add a, LOW(wNewlineTiles) - ld e, a - adc a, HIGH(wNewlineTiles) - sub e - ld d, a - ld a, [wTextCurTile] - ld [de], a - ; Fall through + ld a, c + and 7 + ld [wTextCurPixel], a + ; Fall through PrintNextCharInstant: - xor a - ld [wTextNextLetterDelay], a - ret - - -FlushVWFBuffer:: - push hl - - ; Calculate ptr to next tile - ld a, [wTextCurTile] - swap a - ld d, a - and $F0 - ld e, a - ld a, d - and $0F - add a, HIGH(vVWFTiles) - ld d, a - - ; Copy buffer 1 to VRAM, buffer 2 to buffer 1, and clear buffer 2 - ld hl, wTextTileBuffer - ld bc, wTextTileBuffer + $10 -.copyByte - ldh a, [rSTAT] - and STATF_BUSY - jr nz, .copyByte - ; Write tile buf to VRAM - ld a, [hl] - ld [de], a - inc e ; Faster than inc de, guaranteed thanks to ALIGN[4] - ; Copy second tile to first one - ld a, [bc] - ld [hli], a - ; Clear second tile - xor a - ld [bc], a - inc c - - ld a, l - cp LOW(wTextTileBuffer + $10) - jr nz, .copyByte - - ; Go to next tile - ld hl, wTextCurTile - ld a, [hl] - inc a - and $7F - jr nz, .noWrap - inc a ; Don't touch tile 0, it must stay blank -.noWrap - ld [hl], a - - ld hl, wFlushedTiles - inc [hl] - pop hl - ret - + xor a + ld [wTextNextLetterDelay], a + ret + + +TextWaitButtonScroll: + call TextWaitButton + ; The function returns with a = 0 iff the user has input something + and a + ret nz + ; fallthrough + +TextScroll: + push hl + + ld b, b ; You'll have to write your own code here + ld hl, vText + ld de, vText + SCRN_VX_B + ld b, TEXT_HEIGHT_TILES - 1 + .shiftTilemapRows + ld c, TEXT_WIDTH_TILES + .shiftRow + ldh a, [rSTAT] + and STATF_BUSY + jr nz, .shiftRow + ld a, [de] + ld [hli], a + inc e + dec c + jr nz, .shiftRow + ld a, e + add a, SCRN_VX_B - TEXT_WIDTH_TILES + ld e, a + adc a, d + sub e + ld d, a + ld hl, -SCRN_VX_B + add hl, de + dec b + jr nz, .shiftTilemapRows + lb bc, 0, TEXT_WIDTH_TILES + call LCDMemsetSmallFromB + + ld hl, wPenPosition + ld a, [hl] + sub SCRN_VX_B + ld [hli], a + jr nc, .noCarry + dec [hl] +.noCarry + pop hl + ; fallthrough +TextNewline: + ; Flush the current tile if non-blank + ld a, [wTextCurPixel] + cp 2 + call nc, FlushVWFBuffer + ; Reset position + xor a + ld [wTextCurPixel], a + + ld de, wNbNewlines + ld a, [de] + inc a + ld [de], a + dec a + add a, LOW(wNewlineTiles) + ld e, a + adc a, HIGH(wNewlineTiles) + sub e + ld d, a + ld a, [wTextCurTile] + ld [de], a + jr PrintNextCharInstant + + +TextSync: + ld a, [wTextFlags] + set 7, a + ld [wTextFlags], a + ret + + +TextClear: + push hl + ;;;; You'll probably want to clear some tilemap here ;;;; + ld b, b + ld hl, vText + ld e, TEXT_HEIGHT_TILES + .clearTilemap + lb bc, 0, TEXT_WIDTH_TILES + call LCDMemsetSmallFromB + ld a, l + add a, SCRN_VX_B - TEXT_WIDTH_TILES + ld l, a + adc a, h + sub l + ld h, a + dec e + jr nz, .clearTilemap + + + ; Reset text printing + ; Don't flush if current tile is empty + ld a, [wTextCurPixel] + cp 2 + ; Flush buffer to VRAM + call nc, FlushVWFBuffer + ; Reset position always, though + xor a + ld [wTextCurPixel], a + ; The pen should not advance, should we have flushed a tile + ld [wFlushedTiles], a + ld [wNbNewlines], a + + ;;;; You will probably want to reset the pen position, too ;;;; + ld b, b + ld hl, vText + call SetPenPosition + + pop hl + ret + + +SECTION "Charset data", ROM0 + +IF !DEF(NB_CHARSETS) + FAIL "Please define NB_CHARSETS!" +ENDC CharsetPtrs:: - dw LatinCharsetBasic - ; TODO: add more charsets (bold, JP, etc.) - - -LatinCharsetBasic:: - dw .space - - dw .exclMark - dw .quote - dw .hash - dw .space ; $ - dw .space ; % - dw .amp - dw .apos - dw .openBrack - dw .closeBrack - dw .star - dw .plus - dw .comma - dw .dash - dw .dot - dw .slash - - dw .zero - dw .one - dw .two - dw .three - dw .four - dw .five - dw .six - dw .seven - dw .eight - dw .nine - - dw .colon - dw .halfColon - dw .lt - dw .equal - dw .gt - dw .questMark - dw .space ; @ - - dw .A - dw .B - dw .C - dw .D - dw .E - dw .F - dw .G - dw .H - dw .I - dw .J - dw .K - dw .L - dw .M - dw .N - dw .O - dw .P - dw .Q - dw .R - dw .S - dw .T - dw .U - dw .V - dw .W - dw .X - dw .Y - dw .Z - - dw .openSqBrack - dw .backslash - dw .closeSqBrack - dw .caret - dw .underscore - dw .backtick - - dw .a - dw .b - dw .c - dw .d - dw .e - dw .f - dw .g - dw .h - dw .i - dw .j - dw .k - dw .l - dw .m - dw .n - dw .o - dw .p - dw .q - dw .r - dw .s - dw .t - dw .u - dw .v - dw .w - dw .x - dw .y - dw .z - - dw .openCurlBrack - dw .pipe - dw .closeCurlBrack - - dw .space ; DEL - -REPT " " - dw .space -ENDR - - -.space - db $00, $00, $00, $00, $00, $00, $00, $00, 2 -.exclMark - db $40, $40, $40, $40, $40, $00, $40, $00, 5 -.quote - db $D8, $90, $00, $00, $00, $00, $00, $00, 6 -.hash - db $50, $F8, $50, $50, $50, $F8, $50, $00, 6 -.amp - db $40, $A0, $60, $40, $A0, $A0, $50, $00, 4 -.apos - db $C0, $40, $80, $00, $00, $00, $00, $00, 3 -.openBrack - db $40, $80, $80, $80, $80, $80, $40, $00, 3 -.closeBrack - db $80, $40, $40, $40, $40, $40, $80, $00, 3 -.star - db $00, $20, $A8, $70, $A8, $20, $00, $00, 6 -.plus - db $00, $20, $20, $F8, $20, $20, $00, $00, 6 -.comma - db $00, $00, $00, $00, $00, $00, $40, $80, 3 -.dash - db $00, $00, $00, $F0, $00, $00, $00, $00, 5 -.dot - db $00, $00, $00, $00, $00, $00, $80, $00, 2 -.slash - db $00, $10, $20, $20, $40, $40, $80, $00, 5 - -.zero - db $60, $90, $B0, $D0, $90, $90, $60, $00, 5 -.one - db $C0, $40, $40, $40, $40, $40, $40, $00, 3 -.two - db $E0, $10, $10, $20, $40, $80, $F0, $00, 5 -.three - db $E0, $10, $10, $60, $10, $10, $E0, $00, 5 -.four - db $30, $50, $90, $F0, $10, $10, $10, $00, 5 -.five - db $F0, $80, $80, $E0, $10, $10, $E0, $00, 5 -.six - db $60, $80, $80, $E0, $90, $90, $60, $00, 5 -.seven - db $F0, $10, $10, $20, $20, $40, $40, $00, 5 -.eight - db $60, $90, $90, $60, $90, $90, $60, $00, 5 -.nine - db $60, $90, $90, $70, $10, $10, $60, $00, 5 - -.colon - db $00, $80, $80, $00, $80, $80, $00, $00, 2 -.halfColon - db $00, $40, $40, $00, $40, $40, $80, $00, 3 -.lt - db $00, $10, $60, $80, $60, $10, $00, $00, 5 -.equal - db $00, $00, $F0, $00, $F0, $00, $00, $00, 5 -.gt - db $00, $80, $60, $10, $60, $80, $00, $00, 5 -.questMark - db $60, $90, $10, $20, $40, $00, $40, $00, 5 - -.A db $60, $90, $90, $F0, $90, $90, $90, $00, 5 -.B db $E0, $90, $90, $E0, $90, $90, $E0, $00, 5 -.C db $70, $80, $80, $80, $80, $80, $70, $00, 5 -.D db $E0, $90, $90, $90, $90, $90, $E0, $00, 5 -.E db $F0, $80, $80, $E0, $80, $80, $E0, $00, 5 -.F db $F0, $80, $80, $F0, $80, $80, $80, $00, 5 -.G db $70, $80, $80, $B0, $90, $90, $60, $00, 5 -.H db $90, $90, $90, $F0, $90, $90, $90, $00, 5 -.I db $E0, $40, $40, $40, $40, $40, $E0, $00, 4 -.J db $10, $10, $10, $10, $10, $10, $E0, $00, 5 -.K db $90, $90, $90, $A0, $E0, $90, $90, $00, 5 -.L db $80, $80, $80, $80, $80, $80, $F0, $00, 5 -.M db $88, $D8, $A8, $88, $88, $88, $88, $00, 6 -.N db $90, $D0, $B0, $90, $90, $90, $90, $00, 5 -.O db $60, $90, $90, $90, $90, $90, $60, $00, 5 -.P db $E0, $90, $90, $E0, $80, $80, $80, $00, 5 -.Q db $60, $90, $90, $90, $90, $A0, $70, $00, 5 -.R db $E0, $90, $90, $E0, $A0, $90, $90, $00, 5 -.S db $70, $80, $80, $60, $10, $10, $E0, $00, 5 -.T db $F8, $20, $20, $20, $20, $20, $20, $00, 6 -.U db $90, $90, $90, $90, $90, $90, $60, $00, 5 -.V db $90, $90, $90, $90, $90, $A0, $40, $00, 5 -.W db $88, $88, $88, $88, $A8, $D8, $88, $00, 6 -.X db $90, $90, $90, $60, $90, $90, $90, $00, 5 -.Y db $90, $90, $90, $A0, $40, $40, $40, $00, 5 -.Z db $F0, $10, $20, $40, $80, $80, $F0, $00, 5 - -.openSqBrack - db $C0, $80, $80, $80, $80, $80, $C0, $00, 3 -.backslash - db $00, $80, $40, $40, $20, $20, $10, $00, 5 -.closeSqBrack - db $C0, $40, $40, $40, $40, $40, $C0, $00, 3 -.caret - db $40, $00, $00, $00, $00, $00, $00, $00, 4 -.underscore - db $00, $00, $00, $00, $00, $00, $00, $F0, 5 -.backtick - db $80, $40, $00, $00, $00, $00, $00, $00, 3 - -.a db $00, $00, $60, $10, $70, $90, $60, $00, 5 -.b db $00, $80, $80, $E0, $90, $90, $60, $00, 5 -.c db $00, $00, $70, $80, $80, $80, $70, $00, 5 -.d db $00, $10, $10, $70, $90, $90, $60, $00, 5 -.e db $00, $00, $60, $90, $E0, $80, $60, $00, 5 -.f db $00, $70, $80, $F0, $80, $80, $80, $00, 5 -.g db $00, $00, $60, $90, $90, $70, $10, $E0, 5 -.h db $00, $80, $80, $E0, $90, $90, $90, $00, 5 -.i db $00, $80, $00, $80, $80, $80, $80, $00, 2 -.j db $00, $20, $00, $20, $20, $20, $C0, $00, 4 -.k db $00, $80, $90, $A0, $C0, $A0, $90, $00, 5 -.l db $00, $80, $80, $80, $80, $80, $80, $00, 2 -.m db $00, $00, $D0, $A8, $A8, $A8, $A8, $00, 6 -.n db $00, $00, $E0, $90, $90, $90, $90, $00, 5 -.o db $00, $00, $60, $90, $90, $90, $60, $00, 5 -.p db $00, $00, $60, $90, $90, $E0, $80, $80, 5 -.q db $00, $00, $60, $90, $90, $70, $10, $10, 5 -.r db $00, $00, $B0, $C0, $80, $80, $80, $00, 5 -.s db $00, $00, $70, $80, $60, $10, $E0, $00, 5 -.t db $00, $40, $F0, $40, $40, $40, $30, $00, 5 -.u db $00, $00, $90, $90, $90, $90, $60, $00, 5 -.v db $00, $00, $90, $90, $90, $A0, $40, $00, 5 -.w db $00, $00, $88, $88, $88, $A8, $D0, $00, 6 -.x db $00, $00, $B0, $40, $40, $40, $B0, $00, 5 -.y db $00, $00, $90, $90, $90, $70, $10, $E0, 5 -.z db $00, $00, $F0, $10, $60, $80, $F0, $00, 5 - -.openCurlBrack - db $ -.pipe - db $80, $80, $80, $80, $80, $80, $80, $00, 2 -.closeCurlBrack - db $ + rsreset + REPT NB_CHARSETS +CHARSET equs "CHARSET_{d:_RS}" +CHARSET_DEFINED equs "DEF({CHARSET})" + + IF CHARSET_DEFINED +CHARSET_LABEL equs "Charset{d:_RS}" + dw CHARSET_LABEL + PUSHS +SECTION "Charset {d:_RS}", ROM0 +CHARSET_LABEL: + INCBIN "{{CHARSET}}" + IF @ - CHARSET_LABEL > CHARACTER_SIZE * NB_FONT_CHARACTERS + WARN "Charset {d:_RS} is larger than expected; keep in mind they can only contain {d:NB_FONT_CHARACTERS} characters" + ENDC + POPS + PURGE CHARSET_LABEL + + ELSE + dw 0 + ENDC + PURGE CHARSET_DEFINED + PURGE CHARSET + rsset _RS + 2 + ENDR + + +SECTION "VWF engine memory", WRAM0,ALIGN[7] + +wTextCharBuffer:: + ds 64 +wTextCharBufferEnd:: ; We need this not to be on a 256-byte boundary + assert HIGH(wTextCharBuffer) == HIGH(wTextCharBufferEnd) + + assert @ & -(1 << 6) +wTextTileBuffer:: + ds $10 * 2 + + assert @ & -(1 << 5) +; Format of entries: ptr(16bit LE), bank +wTextStack:: + ds TEXT_STACK_CAPACITY * 3 +; Number of entries in the stack +wTextStackSize:: + db + + +wTextCurPixel:: + db +wTextCurTile:: + db +; ID of the last tile the VWF engine is allowed to write to +wLastTextTile:: + db +; Tells which tile to wrap to when going past the above +wWrapTileID:: + db +; This allows selecting the "tile block" to use +; Write $80 for tiles in $8000-8FFF +; Write $90 for tiles in $9000-97FF +; Other values are not officially supported, experiment yourself +wTextTileBlock:: + db + +; Tells which color to use in the palette for the text (in range 0-3) +wTextColorID:: + db + +; Defines which character table to use +; Upper nibble is language-defined, lower nibble is decoration +wTextCharset:: + db + +wPreviousFont:: + db +wPreviousVariant:: + db + +wTextSrcPtr:: + dw +wTextSrcBank: + db + +; Number of frames between each character +wTextLetterDelay:: + db +; Number of frames till next character +wTextNextLetterDelay:: + db + +; Bit 6 - Whether the text engine is currently waiting for a button press +; Bit 7 - Whether the text engine is halted, for syncing (can be reset) +wTextFlags:: + db + +; Number of tiles flushed, used to know how many tiles should be written to the tilemap +wFlushedTiles:: + db + +; Number of newlines that occurred during this print +wNbNewlines:: + db +; ID of the tiles during which newlines occurred (NOTE: there can be duplicates due to empty lines!!) +wNewlineTiles:: + ds TEXT_NEWLINE_CAPACITY + +wPenStartingPosition:: + dw +wPenPosition:: + dw +wPenCurTile:: + db + +; Low byte of the read ptr into wTextCharBuffer +wTextReadPtrLow:: + db +; Where the refiller ended, i.e. where the printer needs to stop +wTextReadPtrEnd:: + db +; Where the refiller's read ended; characters between the above and this may be candidate for an +; auto linebreak, so they shouldn't be passed to the printer yet +wTextFillPtrEnd:: + db + +; Number of lines of the current text area +wTextNbLines:: + db +; How many newlines remain before the text box is full +wTextRemainingLines:: + db +; How many newlines remain until the text box has been filled with fresh text +wNewlinesUntilFull:: + db +; Length, in pixels, of the current text line +wTextLineLength:: + db +wLineRemainingPixels:: + db +; Ptr to last newlineable location +wNewlinePtrLow:: + db +; wLineRemainingPixels at the time wNewlinePtrLow is updated +wPixelsRemainingAtNewline:: + db +; Charset ptr is cached by refiller to speed up reads +wCurCharsetPtr:: + dw +wRefillerCharset:: + db +wRefillerPrevFont:: + db +wRefillerPrevVariant:: + db + +SECTION "VWF engine fast memory", HRAM + +; How many rows are left to be drawn in the current tile +hVWFRowCount:: + db