numbers.asm - RLH-2110/gbCalc GitHub Wiki
Overview
validateInput
Sign routines
Display routines
Miscellaneous
ConvertInputs
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, ...
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.
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
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
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
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
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
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