I'm replying to NightWolve's question here to avoid polluting the original thread ...
Quote from: NightWolve on 08/20/2015, 06:54 PMQuote from: elmer on 08/20/2015, 04:54 PMCustom VWF font (with added drop-shadow) ...
(https://farm6.staticflickr.com/5659/20554827189_e6f1a57c4a_o.png) (https://flic.kr/p/xjmS1e)
Impressive work to be sure! How did you pull that off ? Like, say, how much PC-FX assembly code did you write/mod to accomplish the task ?
I'd love to be able to say wonderful things about how smart I am in hacking a VWF into Team Innocent ... but I'm afraid that I can't.
The truth is, that what you're seeing in that screenshot isn't a "true" VWF ... it's a "BWF", a bi-width font.
The characters that are displayed are either 4x16, or 8x16.
The actual font itself is designed in such a way as to minimize the clues that would give away that fact that it isn't a "true" VWF. :-"
Now, the reason for doing this instead of a real VWF, is that Team Innocent writes all of it's strings to a "virtual" screen in 4x8 pixel chunks, and then uploads any "changes" to VRAM in a totally separate "task" that runs every-now-and-again.
I couldn't figure out how to hack a VWF into such a bizarre scheme, but it did eventually occur to me that although the code normally just prints 8-wide or 12-wide fonts (2 or 3 4-pixel chunks), there was no reason that it couldn't print single chunk wide characters (i.e. just 4-pixels).
So, the entire "VWF" hack was just telling it that certain characters should be printed with a width of 1 4-pixel chunk instead of 2 or 3 4-pixel chunks.
Then it was just a case of hacking in a custom set of font data that took advantage of the new capability.
So, the new "VWF" code is just ...
_get_glyph_width:
movea 0x3fff, r0 ,r1 // glyph $8000-$ffff (12x12)
cmp r1, r27
mov 3, r28 // width=3
bgt 1$
movea 0x1fff, r0 ,r1 // glyph $0000-$00ff (8x8)
cmp r1, r27
mov 2, r28 // width=2
ble 1$
addi -' ', r27, r1 // glyph $2000-$20ff (6x12)
andi 0x7f, r1, r1
movea lo(_step8x14v), r1, r1 // read the width (in 4-pixel chunks)
movhi hi(_step8x14v), r1, r1 // from a table.
ld.b 0[r1], r28
1$: jr _got_glyph_widthIf that looks too strange, this might help ...
V810 CPU Architecture Manual
http://www.goliathindustries.com/vb/download/cpu/U10082EJ1V0UM00.pdf (http://www.goliathindustries.com/vb/download/cpu/U10082EJ1V0UM00.pdf)
Now, as to "how" I actually got to the point of knowing how the Team Innocent code was working in that strange way, and exactly where to hack in that "solution".
I'm afraid that that's just the usual boring hacking job of identifying interesting-looking memory accesses, and then tracing and hand-disassembling a few thousand instructions back into their equivalent 'C' code to understand what they're doing. :(
What seems a little sad, is that while Zeroigar has "proper" VWF font printing, with kerning, too ... the truth is that most people won't notice the difference.
And that was a significantly larger and much more complex piece of code to write!
I think that the big thing for anyone reading this to take away, and a good lesson that I'd totally forgotten myself, is that you sometimes don't need the "perfect" solution in order to get something that actually looks good on the screen. :wink:
Yes, interesting. It's got a futuristic-looking font where everything is the same space and would be fine in monospace... except for l, i, t...
Quote from: elmer on 08/21/2015, 02:20 AMI'm replying to NightWolve's question here to avoid polluting the original thread ...
Quote from: NightWolve on 08/20/2015, 06:54 PMQuote from: elmer on 08/20/2015, 04:54 PMCustom VWF font (with added drop-shadow) ...
(https://farm6.staticflickr.com/5659/20554827189_e6f1a57c4a_o.png) (https://flic.kr/p/xjmS1e)
Impressive work to be sure! How did you pull that off ? Like, say, how much PC-FX assembly code did you write/mod to accomplish the task ?
I'd love to be able to say wonderful things about how smart I am in hacking a VWF into Team Innocent ... but I'm afraid that I can't.
The truth is, that what you're seeing in that screenshot isn't a "true" VWF ... it's a "BWF", a bi-width font.
...
I think that the big thing for anyone reading this to take away, and a good lesson that I'd totally forgotten myself, is that you sometimes don't need the "perfect" solution in order to get something that actually looks good on the screen. :wink:
That's an elegant solution. :)
Ahh. So it's a hybrid. Looks great.
Now, if anyone is curious what real VWF code looks like on the PC-FX, here's an example.
Remember that the PC-FX uses the same VDC chip as the PCE, so you can directly compare this to how you'd do it on the PCE, the output data is identical.
This is the code that I wrote to produce the yellow "speech" text in Zeroigar.
The V810 is a RISC processor, so some of the instruction ordering looks a little strange in order to avoid pipeline stalls.
(https://farm1.staticflickr.com/511/19987867500_362dd2a997_o.png) (https://flic.kr/p/wsg3Db)
//
// ****************************************************************************
//
// _print_6x14_015_vwf_glyph
//
// r6 = VDC I/O address (VDC-A=$400, VDC-B=$500)
// r7 = glyph number
// r8 = X lhs-coordinate (0..255) | (X rhs-coordinate << 16)
// r9 = VRAM address of start of row
//
// N.B. Each row is stored as 'n' columns of 8x16 (i.e. 1x2 chr).
//
#define rVdcPtr r6 // PRESERVED!!!
#define rChrNum r7
#define rXCoord r8 // PRESERVED!!!
#define rDstRow r9 // PRESERVED!!!
#define rCol0Ptr r10
#define rCol1Ptr r11
#define rCol2Ptr r12 // Only if max font width > 9 pixels.
#define rCol3Ptr r13 // Only if max font width > 17 pixels.
#define rOldShd r14
#define rCurFnt r15
#define rCurShd r16
#define rCurBkg r17
#define rLinCnt r18
#define rRotCnt r19
#define rFntPtr rChrNum
#define rTmpVal0 rOldShd
#define rTmpVal1 rCurFnt
#define rTmpVal2 rCurShd
#define rTmpVal3 rCurBkg
#define rChrCnt r19
_print_6x14_015_vwf_glyph:
addi -32, rChrNum, rChrNum
mov 14, r1
mulu r1, rChrNum
movea lo(_font6x14v), rChrNum, rFntPtr
movhi hi(_font6x14v), rFntPtr, rFntPtr
_print_015_vwf_glyph:
add -4, sp
st.w lp, 0[sp]
andi 0x0018, rXCoord, rCol0Ptr
shl 3, rCol0Ptr
movea 0x0040, rCol0Ptr, rCol1Ptr
andi 0x00c0, rCol1Ptr, rCol1Ptr
movhi hi(_workspace0), r0, r1 // 32x16 4-bpp workspace,
movea lo(_workspace0), r1, r1 // 256-byte long, 16-byte aligned.
add r1, rCol0Ptr
add r1, rCol1Ptr
andi 0x0007, rXCoord, rRotCnt // Calc rotation + 16. This prints 2
addi 16, rRotCnt, rRotCnt // columns (max font width <= 9 pixels).
mov r0, rOldShd // If we're on pixel 0 of a new chr, then
bne 2$ // it's not *guaranteed* to be blank.
mov rCol0Ptr, r1 // Clear the current chr's workspace.
mov (64/8), r30
1$: st.w r0, 0x00[r1]
add -1, r30
st.w r0, 0x04[r1]
movea 8, r1, r1
bnz 1$
2$: mov 14, rLinCnt // Glyph height.
add 2, rCol1Ptr // Center 14-pixel high glyph in
add 2, rCol0Ptr // 16-pixel high buffer.
3$: ld.b 0[rFntPtr], rCurFnt // Get the next line of font data.
add 1, rFntPtr
shl 24, rCurFnt
mov rCurFnt, rCurShd // Get the next line of shadow data.
shr 1, rCurShd
or rCurFnt, rCurShd
4$: mov rOldShd, r1 // Add in previous line's shadow data
mov rCurShd, rOldShd // to make it a "drop-shadow".
or r1, rCurShd
// font data pixel = color 5
// font drop shadow = color 1
// background color = color 0
// Process RHS column.
st.b r0, 0x01[rCol1Ptr] // rhs plane 1 (cleared)
st.b r0, 0x11[rCol1Ptr] // rhs plane 3 (cleared)
shr rRotCnt, rCurShd
st.b rCurShd, 0x00[rCol1Ptr] // rhs plane 0 (set on font or shadow)
shr rRotCnt, rCurFnt
st.b rCurFnt, 0x10[rCol1Ptr] // rhs plane 2 (set on font only)
// Process LHS column.
shr 8, rCurShd
ld.b 0x00[rCol0Ptr], r1 // lhs plane 0
or rCurShd, r1
st.b r1, 0x00[rCol0Ptr] // lhs plane 0 (set on font or shadow)
shr 8, rCurFnt
ld.b 0x10[rCol0Ptr], r1 // lhs plane 2
or rCurFnt, r1
st.b r1, 0x10[rCol0Ptr] // lhs plane 2 (set on font only)
add 2, rCol0Ptr
add 2, rCol1Ptr
andi 0x000e, rCol0Ptr, r0 // End of plane 0&1 data?
bnz 5$
movea 16, rCol0Ptr, rCol0Ptr // Skip to next chr down.
movea 16, rCol1Ptr, rCol1Ptr // Skip to next chr down.
5$: add -1, rLinCnt // Draw the next line of font data.
bgt 3$
mov 0, rCurFnt // Draw the bottom line's drop shadow.
mov 0, rCurShd
bz 4$
// Now copy the glyph's columns to vram.
andi 0x00f8, rXCoord, r1 // Setup the VDC write address
shl 2, r1 // (starting row from rDstRow)
out.h r0, 0[r6]
add rDstRow, r1
out.h r1, 4[r6]
mov 2, r1
out.h r1, 0[r6]
mov rXCoord, rChrCnt // Calc (rhs-chr - lhs-chr) to
shr 3, rChrCnt // see how many chrs to upload.
andi 0x1fff, rChrCnt, r1
shr 16, rChrCnt
sub r1, rChrCnt
movea -64, rCol0Ptr, r7
movea (64/4), r0, r1 // Upload LHS column to VRAM.
6$: ld.w 0[r7], rTmpVal0
add 4, r7
add -1, r1
out.w rTmpVal0, 4[r6]
bne 6$
add -1, rChrCnt // Do we need to upload another column?
movea -64, rCol1Ptr, r7
bn 8$
movea (64/4), r0, r1 // Upload RHS column to VRAM.
7$: ld.w 0[r7], rTmpVal0
add 4, r7
add -1, r1
out.w rTmpVal0, 4[r6]
bne 7$
8$: ld.w 0[sp], lp
add 4, sp
jmp [lp]
Thanks for sharing John! Yeah, as I thought, you sure know what you're doing! That wasn't child's play right there when dealing with your real VWF implementation...
We could've *really* used a guy like you ~10-15 years ago for this stuff! Not to mention a PCE/TG-16 emulator with a good internal debugger - I had asked David Michel many times to someday add that to MagicEngine, but he never got around to it.