numbers.asm - RLH-2110/gbCalc GitHub Wiki

Table of Contents

Overview
validateInput
Sign routines
Display routines
Miscellaneous
ConvertInputs

Overview

This file contains routines that work with the numbers in Video RAM. It has routines to display the result, validate the input numbers, resetting the numbers, ...

validateInput

This routine checks if both numbers are valid, and if they are invalid, it sets them to the next valid number.
This is for overflow protection, since we have a maximum number with 16 bit.

Graphic of the jumps

This code is quite confusing with all the jump statements, so I tried to make a graphic for visualisation.

Click Here to see the graphic https://github.com/user-attachments/assets/6540473e-6324-4f2b-a7f4-02605786960a

The Code

we have a lookup table to see what the expected tiles are for the maximum number

; max 32.767
; min -32.768
ValidNumberLookup:
db tile_three, tile_two, tile_seven, tile_six, tile_empty ; last byte will either be 7 or 8, we can modify it when we need to actually check the byte.

First we have the setup for number0

;! assumes that all registers are free to use. !
; uses HL,A,BC, (ram)wTmpL, (ram)wTmpH
validateInput::

	ld hl,screen + num0I ; loads adress of number 0(in tilemap) 
	xor a,a ; a = 0
	ld c,a ;counter in number
	ld [wTmpL],a ; number counter
	ld [wTmpH],a ; boolean, overwrite all
	jr .CheckNumber

after that is a section with the setup for number1

	.nextNumber:
		ld hl,screen + num1I ; loads adress of number 1 (in tilemap) 
		ld c,0 ;counter
		ld a,1
		ld [wTmpL],a ; number counter
		xor a,a ; a = 0
		ld [wTmpH],a ; boolean, overwrite all

This code checks if the tile matches the expected tile. It also finds out what the last expected tile is. It also coordinates overwriting the number, if it's too high to handle with 16 bits.

	.CheckNumber:

		ld b,[hl] ; get the current tile

		;get the expected tile
			push hl
			ld hl,ValidNumberLookup
			
			; add ValidNumberLookup and c
			ld a,l
			add a,c
			ld l,a

			ld a,h
			adc a,0
			ld h,a

			; check if tile is empty

			ld a,[hl]
			cp tile_empty
			jr nz,.CheckTile

			;tile is empty

				;check if number is positive
				call CheckSign_TileMap

				; z = positive, nz = negative
				cp a,0
				jr nz, .numberNegative

				; positive number
				ld a,tile_seven
				jr .CheckTile

				.numberNegative:
				ld a,tile_eight

			.CheckTile:
			pop hl

		; check if we are in overwrite mode (the entire number must be overwritten)
		push af
		ld a,[wTmpH]
		cp a,1
		jr z, .OverwriteAll ; if we are in overwerite mode, jump to it.
		pop af
		
		
		; we are still in check mode

		cp a,b ; a = expected tile, b = current tile
		jr z, .nextTile ; if we equal, then we need to check the next part of the number. (b == a)
		jr nc,.CheckNextNumber ; if we are below the max num, check next number (b <= a)
		
		;tiles dont match! ( b > a)
		push af
		ld a,1
		ld [wTmpH],a
		
		jr .OverwriteAll

This just "moves" to the next tile. If we get out of bounds of the number and have another number to check, then we check number1 If we out OOB and don't have another number to check, we will return later in the code.

.nextTile:
		inc c
		inc hl

		; checks if c is outside the number
		ld a,c
		cp a,5 ;(out of bounds after increment)
		jr z,.CheckNextNumber

		jr .CheckNumber

This just replaces the tile to the expected tile

		.OverwriteAll:
		pop af
		ld [hl],a 	; set to expected tile tile
		jr .nextTile

Either repeats all this for number1 or exists.

		.CheckNextNumber:
		ld a,[wTmpL]
		cp a,0
		jp z,.nextNumber

		ret

Sign routines

This routine sets memory to indicate if the user inputted numbers are positive or negative

; sets the negative variables in ram, used by calc.asm
setNegatives::
	call waitStartVBlank

	push af

	ld a,[screen+num0_prefixI] ; load tile that shows sign
	sub a,tile_add ; substact + (a = 0 if tile = add. tile is nz if its not +) 
	ld [wNumber0Negative],a ; 0 = positive; nz = negative

	ld a,[screen+num1_prefixI]
	sub a,tile_add
	ld [wNumber1Negative],a

	pop af
	ret

This code sets A to the sign of the selected number in wTmpL

; wTmpL: number (0 = number0, not zero = number1)
; a: 0 = positive, $ff = negative
CheckSign_TileMap::

	;check if we are in number 0
	ld a,[wTmpL]
	cp a,0
	jr z,.Number0

	;number1
	ld a,[screen + num1_prefixI]
	jr .NumberEnd
	.Number0
	ld a,[screen + num0_prefixI]
	.NumberEnd:

	; a contains the tile that indicates if a number is positive or negative
	cp a,tile_add
	jr nz, .numberNegative

	; positive number
	ld a,0

	ret

	.numberNegative:
	ld a,$ff

	ret

Display routines

This displays the result to the screen

; will use: A, BC, HL, 	<- (update!)
; and (READ)(RAM) wTmp1,wTmp2, wTmpH
displayResult::
	push de

	xor a,a ; a = 0
  	ld [wPrintResult],a ; we are handleing the print order now, so make sure we dont do the same order twice


	; check if we need to display an error message
	ld a,[wResultError]
	cp a,0
	jp nz, ErrorResult ; if an error occured (like the result being out of boudns), then jump somewhere else

	
	; do display stuff here:

	call waitStartVBlank 

	ld a,[wResultNegative] ; if the number is positve, skup to .positive
	or a,a ; set the flags based on the value of a
	jp z,.positive

	;number negative

	; write a '-' before the number
	ld a,tile_sub
	ld [screen+res_prefixI],a
	jp .printNum

	.positive: ; write an empty tile before the number
	ld a,tile_empty
	ld [screen+res_prefixI],a

	.printNum:


	ld DE,wDoubleDabble ; SOURCE
	ld HL,screen + resI ; DESTINATION
	ld BC,doubleDabbleSize ; BYTES
	call Memcpy

	pop de
	ret

Sets the result to 'EEEEE' in case of an error, like an overflow

ErrorResult: 

	call waitStartVBlank 

	; fill the result with E
	ld hl, screen + resI; destination
	ld bc,5 	; bytes to write
	ld d,tile_e ; value to write
	call SetMem

	pop de ; we are called via displayResult, who pushed DE
	ret

Miscellaneous

Sets the selected number to '00000'

clearSelectedNumber::

	ld a,[wCursorState]
	; if number 0
	cp a,cursorState_Number0
	jr z, .Number0

	cp a,cursorState_Number0Sign
	jr z, .Number0

	; if number 1
	cp a,cursorState_Number1
	jr z, .Number1

	cp a,cursorState_Number1Sign
	jr z, .Number1

	; if not a number
	ret

	.Number0:
	ld hl,screen + num0I ; DESTINATION
	jr .setMemory

	.Number1:
	ld hl,screen + num1I ; DESTINATION

	.setMemory:

	ld bc,5 ; BYTES
	ld d,tile_zero ; VALUE
	call SetMem
	ret

ConvertInputs

This is a routine to convert the BCD[^1] numbers from VRAM into binary numbers in WRAM
We do this by taking every digit and multiplying it by 10 as much as we need to.

First we convert number0:

; output: (ram, dw)wNumber0 , (ram, dw)wNumber1
ConvertInputs::
	push af
	push bc
	push hl
	push de

	; set number0 to 0
	xor a,a ; a = 0
	ld [wNumber0],a
	ld [wNumber0+1],a

	ld c,0 ; digit counter/index
	ld hl, screen + num0I + 4; load last digit from number0 (tilemap)
	.Number0Loop:
		
		ld a,[hl]
		dec a ; convert grapical number into real number (grapical 0 = 1, actuall 0 = 0)

		; de = number
		ld d,0
		ld e,a

		ld b,c ; counter for how many times we want to multiply

		;
		; now we (multiply the number by 10) x times, where x is the position of the number - 1 (positions: for a number 43210)
		;

			push hl

			; hl = number
			ld h,d
			ld l,e

		.number0Mul
			ld a,b
			cp a,0
			jr z, .number0Mul_Done

			call u16_times10			

			dec b
			jr .number0Mul 
		.number0Mul_Done
		
			; number = hl
			ld d,h
			ld e,l
			pop hl
			
		dec hl
		inc c

		; load number in hl 	(little endian)
		push hl

		ld a, [wNumber0+1]
		ld h, a 
		ld a, [wNumber0]
		ld l, a

		; add numbers
		add hl,de

		; store number (little endian)
		ld a,h
		ld [wNumber0+1], a 
		ld a, l
		ld [wNumber0], a

		pop hl

		ld a,c
		cp a,5
		jr nz, .Number0Loop

If the number is negative, we adjust it

		; adjust the number if its negative
		; if the number is not negative, skip the next code
		ld a,[screen + num0_prefixI] 
		cp a,tile_sub
		jp nz,.num0done
		; flip all bits
		ld a,[wNumber0]
		xor a,$ff
		ld [wNumber0],a
		ld a,[wNumber0+1]
		xor a,$ff
		ld [wNumber0+1],a
		; add 1
		ld a,[wNumber0]
		inc a
		ld [wNumber0],a
		
		jp nz,.num0done ; no carry on increment
		; carry on increment
		; add 1 to upper byte
		ld a,[wNumber0+1]
		inc a
		ld [wNumber0+1],a

		.num0done:

Number1 is the same code, just a bit adjusted to work with number1

	; set number1 to 0
	xor a,a ; a = 0
	ld [wNumber1],a
	ld [wNumber1+1],a
	ld c,0 ; digit counter/index
	ld hl, screen + num1I + 4; load last digit from number1 (tilemap)
	call waitStartVBlank ; otherwhise we run into errors due to reading vram outside vblank
	
	.Number1Loop:
		
		ld a,[hl]
		dec a ; convert grapical number into real number (grapical 0 = 1, actuall 0 = 0)
		; de = number
		ld d,0
		ld e,a
		ld b,c ; counter for how many times we want to multiply
		;
		; now we (multiply the number by 10) x times, where x is the position of the number - 1 (positions: for a number 43210)
		;
			push hl
			; hl = number
			ld h,d
			ld l,e
		.number1Mul
			ld a,b
			cp a,0
			jr z, .number1Mul_Done
			call u16_times10			
			dec b
			jr .number1Mul 
		.number1Mul_Done
		
			; number = hl
			ld d,h
			ld e,l
			pop hl
			
		dec hl
		inc c
		; load number in hl 	(little endian)
		push hl
		ld a, [wNumber1+1]
		ld h, a 
		ld a, [wNumber1]
		ld l, a
		; add numbers
		add hl,de
		; store number (little endian)
		ld a,h
		ld [wNumber1+1], a 
		ld a, l
		ld [wNumber1], a
		pop hl
		ld a,c
		cp a,5
		jr nz, .Number1Loop
		; adjust the number if its negative
		; if the number is not negative, skip the next code
		ld a,[screen + num1_prefixI] 
		cp a,tile_sub
		jp nz,.num1done
		; flip all bits
		ld a,[wNumber1]
		xor a,$ff
		ld [wNumber1],a
		ld a,[wNumber1+1]
		xor a,$ff
		ld [wNumber1+1],a

		; add 1
		ld a,[wNumber1]
		inc a
		ld [wNumber1],a
		
		jp nz,.num1done ; no carry on increment
		; carry on increment
		; add 1 to upper byte
		ld a,[wNumber1+1]
		inc a
		ld [wNumber1+1],a
		.num1done:
	pop de
	pop hl
	pop bc
	pop af
	ret

[^1] this is not normal BCD, since 0 starts at 1. but It's still a form of binary coded decimal

⚠️ **GitHub.com Fallback** ⚠️