mirror of https://github.com/nmlgc/ReC98.git
307 lines
7.2 KiB
NASM
307 lines
7.2 KiB
NASM
.386
|
|
locals
|
|
|
|
include libs/master.lib/master.inc
|
|
include th02/main/hud/hud.inc
|
|
include th04/main/hud/popup.inc
|
|
include th02/score.inc
|
|
include th02/gaiji/boldfont.inc
|
|
|
|
SCORE_DIGIT_HIGHEST = SCORE_DIGITS - 1
|
|
; Also ignoring the last digit. (= 61,110 points)
|
|
SCORE_DELTA_FRAME_LIMIT = 6111
|
|
if SCORE_DELTA_FRAME_LIMIT ge 100000
|
|
.err "SCORE_DELTA_FRAME_LIMIT can't have more than 5 decimal digits"
|
|
endif
|
|
|
|
; Declaring everything here rather than inside a segment avoids fixup
|
|
; overflows… yup.
|
|
|
|
extrn _score_lebcd:byte:SCORE_DIGITS
|
|
extrn _hiscore_lebcd:byte:SCORE_DIGITS
|
|
extrn _hiscore_popup_shown:byte
|
|
extrn _score_delta:dword
|
|
extrn _score_delta_frame:dword
|
|
|
|
extrn _FIVE_DIGIT_POWERS_OF_10:word
|
|
|
|
extrn _hud_gaiji_row:byte:SCORE_DIGITS
|
|
|
|
extrn _popup_id_new:byte
|
|
extrn _popup:word
|
|
POPUP_UPDATE_AND_RENDER procdesc near
|
|
|
|
if GAME eq 4
|
|
extrn _score_unused:byte
|
|
extrn _temp_lebcd:byte
|
|
|
|
MAIN_01 group MAIN_0_TEXT
|
|
MAIN_0_TEXT segment byte public 'CODE' use16
|
|
SCORE_EXTEND_UPDATE procdesc near
|
|
MAIN_0_TEXT ends
|
|
else
|
|
_temp_lebcd equ _hud_gaiji_row
|
|
endif
|
|
|
|
MAIN_01 group MAIN_01_TEXT
|
|
|
|
; ----------------------------------------------------------------------------
|
|
|
|
MAIN_01_TEXT segment word public 'CODE' use16
|
|
assume cs:MAIN_01
|
|
|
|
; ============================================================================
|
|
|
|
; Renders both the current and high score. In contrast to TH02, it also
|
|
; displays the continues digit.
|
|
|
|
; void pascal near hud_score_put(void);
|
|
public HUD_SCORE_PUT
|
|
HUD_SCORE_PUT proc pascal near
|
|
@@gaiji_p equ bx
|
|
@@score_p equ si
|
|
@@y equ di
|
|
|
|
push si
|
|
push di
|
|
|
|
mov @@score_p, offset _hiscore_lebcd[SCORE_DIGIT_HIGHEST]
|
|
mov @@y, 4
|
|
|
|
@@lebcd_to_gaiji:
|
|
mov cx, SCORE_DIGITS
|
|
mov @@gaiji_p, offset _hud_gaiji_row
|
|
|
|
@@digit_loop:
|
|
mov al, [@@score_p]
|
|
add al, GB_DIGITS
|
|
mov [@@gaiji_p], al
|
|
inc @@gaiji_p
|
|
dec @@score_p
|
|
loop @@digit_loop
|
|
call GAIJI_PUTSA pascal, HUD_LEFT, @@y, ds, offset _hud_gaiji_row, TX_WHITE
|
|
add @@y, 2
|
|
|
|
; Put exactly two lines, high score at (56, 4), and current score at
|
|
; (56, 6).
|
|
; You might notice that @@score_p is only assigned once. Yes, the code
|
|
; assumes that @@score_p now points at the end of _score_lebcd, which in
|
|
; turn assumes it's placed exactly before _hiscore_lebcd in memory, with
|
|
; no padding.
|
|
cmp @@y, 6
|
|
jz @@lebcd_to_gaiji
|
|
|
|
; And if that wasn't enough already, ZUN pops the registers in the wrong
|
|
; order. Good thing it doesn't matter for any caller of this function!
|
|
;
|
|
; This could have easily been fixed by either defining two (or better,
|
|
; three) LOCAL variables, or the USES directive if you *really* insist on
|
|
; using registers. Both of which would have automatically inserted the
|
|
; correct cleanup instructions before RET. Even in the 90's, "using an
|
|
; assembler" did very much *not* mean "having to manually spell out every
|
|
; instruction executed on the CPU"…
|
|
pop si
|
|
pop di
|
|
ret
|
|
HUD_SCORE_PUT endp
|
|
|
|
; ============================================================================
|
|
|
|
; void pascal near score_update_and_render(void);
|
|
public SCORE_UPDATE_AND_RENDER
|
|
SCORE_UPDATE_AND_RENDER proc near
|
|
|
|
; The TH04 version is functionally identical, just less optimized.
|
|
if GAME eq 5
|
|
@@delta_remaining_word equ dx
|
|
@@delta_remaining_char equ dl
|
|
else
|
|
@@delta_remaining_word equ cx
|
|
@@delta_remaining_char equ cl
|
|
endif
|
|
|
|
mov eax, _score_delta
|
|
or eax, eax
|
|
jz short @@ret
|
|
cmp _score_delta_frame, eax
|
|
jbe short @@calculate_frame_delta
|
|
mov word ptr _score_delta_frame, ax
|
|
|
|
@@calculate_frame_delta:
|
|
shr eax, 5
|
|
or eax, eax
|
|
jnz short @@clamp_delta_to_frame_limit
|
|
inc ax
|
|
jmp short @@prefer_larger_delta
|
|
|
|
@@clamp_delta_to_frame_limit:
|
|
cmp eax, SCORE_DELTA_FRAME_LIMIT
|
|
jbe short @@prefer_larger_delta
|
|
mov ax, SCORE_DELTA_FRAME_LIMIT
|
|
|
|
@@prefer_larger_delta:
|
|
cmp word ptr _score_delta_frame, ax
|
|
jnb short @@commit_frame_delta
|
|
mov word ptr _score_delta_frame, ax
|
|
|
|
@@commit_frame_delta:
|
|
mov @@delta_remaining_word, word ptr _score_delta_frame
|
|
jmp short @@update
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@render:
|
|
cmp _hiscore_popup_shown, 0
|
|
jnz short @@subtract_frame_delta_and_render
|
|
or al, al
|
|
jz short @@subtract_frame_delta_and_render
|
|
mov _hiscore_popup_shown, 1
|
|
mov _popup_id_new, POPUP_ID_HISCORE_ENTRY
|
|
mov _popup, offset POPUP_UPDATE_AND_RENDER
|
|
|
|
@@subtract_frame_delta_and_render:
|
|
mov eax, _score_delta_frame
|
|
sub _score_delta, eax
|
|
call HUD_SCORE_PUT
|
|
if GAME eq 4
|
|
mov _score_unused, 0
|
|
call SCORE_EXTEND_UPDATE
|
|
endif
|
|
|
|
@@ret:
|
|
retn
|
|
; ---------------------------------------------------------------------------
|
|
even
|
|
|
|
@@update:
|
|
push si
|
|
push di
|
|
if GAME eq 5
|
|
push ds
|
|
pop es
|
|
|
|
@@bcd_p equ di
|
|
@@po10_p equ bx
|
|
|
|
; Since the delta can have at most 5 digits, we only have to work on the
|
|
; range from _temp_lebcd[4] (highest) to _temp_lebcd[0] (lowest).
|
|
; Zeroing the remaining 3 digits is simply more convenient to do in a
|
|
; single 32-bit instruction if _temp_lebcd[4] is also (unnecessarily)
|
|
; zeroed.
|
|
mov dword ptr _temp_lebcd[4], 0
|
|
else
|
|
@@bcd_p equ bx
|
|
@@po10_p equ si
|
|
endif
|
|
mov @@bcd_p, offset _temp_lebcd[4]
|
|
mov @@po10_p, offset _FIVE_DIGIT_POWERS_OF_10
|
|
if GAME eq 5
|
|
; 4 divisions, the units place doesn't need a separate one.
|
|
mov cx, 4
|
|
endif
|
|
|
|
@@delta_to_bcd:
|
|
mov ax, @@delta_remaining_word
|
|
; 16-bit DIV interprets DX:AX as a 32-bit divisor…
|
|
xor dx, dx
|
|
; … and returns the remainder in DX, so we actually aren't losing
|
|
; anything here.
|
|
div word ptr [@@po10_p]
|
|
if GAME eq 5
|
|
add @@po10_p, 2
|
|
else
|
|
mov @@delta_remaining_word, dx
|
|
endif
|
|
|
|
mov [@@bcd_p], al
|
|
dec @@bcd_p
|
|
|
|
if GAME eq 5
|
|
loop @@delta_to_bcd
|
|
else
|
|
add @@po10_p, 2
|
|
cmp word ptr [@@po10_p], 1
|
|
ja @@delta_to_bcd
|
|
endif
|
|
|
|
@@last_digit:
|
|
mov [@@bcd_p], @@delta_remaining_char
|
|
|
|
; Obviously skipping the continues digit…
|
|
mov si, offset _score_lebcd[1]
|
|
; … and the last one seen from there doesn't need special BCD treatment
|
|
; either.
|
|
mov cx, SCORE_DIGITS - 2
|
|
; Yes, completely unnecessary in TH05, considering the next instruction.
|
|
xor ah, ah
|
|
|
|
; @@bcd_p == _temp_lebcd[0]
|
|
@@add_next_digit_to_score:
|
|
if GAME eq 5
|
|
movzx ax, byte ptr [@@bcd_p]
|
|
else
|
|
mov al, [@@bcd_p]
|
|
endif
|
|
add al, [si]
|
|
aaa ; AL < 9, AH = carry
|
|
mov [si], al
|
|
inc @@bcd_p
|
|
inc si
|
|
; Add the carry to next score digit. May now be 0Ah, but who cares, it's
|
|
; fixed via AAA on the next digit anyway… *except* if it's already the
|
|
; highest one, which is exactly where the infamous >100 million score
|
|
; glitch comes from.
|
|
add [si], ah
|
|
if GAME eq 4
|
|
mov ah, 0
|
|
endif
|
|
loop @@add_next_digit_to_score
|
|
mov al, [@@bcd_p]
|
|
add [si], al
|
|
|
|
; Is this the high score?
|
|
@@is_hiscore equ dl ; Never read, though
|
|
|
|
if GAME eq 4
|
|
push ds
|
|
pop es
|
|
; assume es:_DATA
|
|
endif
|
|
mov si, offset _score_lebcd[SCORE_DIGIT_HIGHEST]
|
|
mov di, offset _hiscore_lebcd[SCORE_DIGIT_HIGHEST]
|
|
xor @@is_hiscore, @@is_hiscore
|
|
mov cx, SCORE_DIGITS
|
|
cmp _hiscore_popup_shown, 0
|
|
jnz short @@hiscore_confirmed
|
|
|
|
@@check_next_digit_for_hiscore:
|
|
mov al, [si]
|
|
cmp [di], al
|
|
ja short @@hiscore_denied
|
|
jb short @@hiscore_confirmed
|
|
dec di
|
|
dec si
|
|
loop @@check_next_digit_for_hiscore
|
|
|
|
@@hiscore_confirmed:
|
|
; Copy the remaining number of digits in CX, backwards (STD!)
|
|
if GAME eq 5
|
|
cli
|
|
endif
|
|
std
|
|
rep movsb
|
|
cld
|
|
if GAME eq 5
|
|
sti
|
|
endif
|
|
inc @@is_hiscore
|
|
|
|
@@hiscore_denied:
|
|
mov al, @@is_hiscore
|
|
pop di
|
|
pop si
|
|
jmp @@render
|
|
SCORE_UPDATE_AND_RENDER endp
|
|
MAIN_01_TEXT ends
|
|
|
|
end
|