diff --git a/assets/page1.chr b/assets/page1.chr index c3dd380..bdd1250 100644 Binary files a/assets/page1.chr and b/assets/page1.chr differ diff --git a/compile.bat b/compile.bat index 4b04d8f..951c71a 100644 --- a/compile.bat +++ b/compile.bat @@ -1,5 +1,7 @@ @ECHO off +:begin + ECHO. ECHO Compiling main.asm ECHO rgbasm -o main.o main.asm @@ -14,9 +16,9 @@ IF %ERRORLEVEL% NEQ 0 ( ECHO. ECHO Linking main.o -ECHO rgblink -o test.gb main.o +ECHO rgblink -o test.gb -m test.map -n test.sym main.o -C:\Users\jonatan.SAMSYN\Documents\gb\tools\compiler\rgblink -o test.gb main.o +C:\Users\jonatan.SAMSYN\Documents\gb\tools\compiler\rgblink -o test.gb -m test.map -n test.sym main.o IF %ERRORLEVEL% NEQ 0 ( ECHO. @@ -43,3 +45,5 @@ ECHO DONE :eof ECHO. PAUSE + +GOTO begin diff --git a/game/game.asm b/game/game.asm index 89ac733..396c851 100644 --- a/game/game.asm +++ b/game/game.asm @@ -1,14 +1,22 @@ -INCLUDE "game/string.asm" +INCLUDE "game/text.asm" SECTION "Game Loop", ROM0 GameLoop: - ld b, 0 - ld c, 0 - ld hl, HelloWorldStr - call PrintString -.lockup +.wait ld a, [rLY] ; Load the current vertical line being rendered cp 144 ; Check if we're past VBlank (1 to 144 = carry bit set) - jr c, .lockup ; Jump back to waitVBlank if rLY is less than 144 - jr .lockup + jr c, .wait ; Jump back to waitVBlank if rLY is less than 144 + + ld de, HelloWorldStr + call StartText +.mainloop + ; Start our game loop + call DrawActiveText + call WaitVBlank + jr .mainloop + +Section "Hello wrold string", ROM0 + +HelloWorldStr: + db "Hello, Worl'd.!?", 0 diff --git a/game/string.asm b/game/string.asm deleted file mode 100644 index 5e63643..0000000 --- a/game/string.asm +++ /dev/null @@ -1,25 +0,0 @@ -SECTION "String code", ROM0 -; Print String on screen -; @param b - PosX on where to draw text -; @param c - PosY on where to draw text -; @param hl - Address source string -; @return a -; @return b -; @return c -; @return de - Destination memory in map -; @return hl = Address Target to the byte after the last byte on the target (garbage) -PrintString: - ld de, $9800 -.start - ld a, [hl] ; Load the first byte in the string - inc hl ; Increment immediately to next space - ld [dei], a - and a - jr nz, .start - ret -.render -.finish - inc b - cp b, 14 - - diff --git a/game/text.asm b/game/text.asm new file mode 100644 index 0000000..25fde2b --- /dev/null +++ b/game/text.asm @@ -0,0 +1,190 @@ +SECTION "ram_textwriter", WRAM0 +textwriter_curtext: ds 2 +textwriter_curletter: ds 1 +textwriter_posx: ds 1 +textwriter_posy: ds 1 +textwriter_curdelay: ds 1 +textwriter_active: ds 1 + +SECTION "ram_textmap", ROM0,ALIGN[8] +textwriter_map: + ; 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; 0 + ; 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; 16 + ; 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + db $00,$41,$00,$00,$00,$00,$00,$43,$00,$00,$00,$00,$42,$00,$3F,$00 ; 32 + ; 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + db $35,$36,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$00,$00,$00,$00,$00,$40 ; 48 + ; 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + db $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$0B,$0C,$0D,$0E,$0F ; 64 + ; 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + db $10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$1A,$00,$00,$00,$00,$00 ; 80 + ; 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F + db $00,$1B,$1C,$1D,$1E,$1F,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29 ; 96 + ; 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F + db $2A,$2B,$2C,$2D,$2E,$2F,$30,$31,$32,$33,$34,$00,$00,$00,$00,$00 ; 112 + +SECTION "TextWriter", ROM0 + +; Start to print a string on screen +; @param b - PosX on where to draw text +; @param c - PosY on where to draw text +; @param de - Address source string +; @return a - garbage +; @return b - PosX on where to draw text +; @return c - PosY on where to draw text +; @return hl - Workram position for text pointer +; @return de - Memory address to source string +StartText: + ld a, 0 + ld [textwriter_curletter], a + ld [textwriter_curdelay], a + ld a, 1 + ld [textwriter_active], a + ld [textwriter_posx], a + ld [textwriter_posy], a + ld hl, textwriter_curtext + ld a, d + ld [hli], a + ld a, e + ld [hl], a + dec hl + ret + +; Draw text if one is currently active +; @return a - 0 if not active, 1 if active +DrawActiveText: + ld a, [textwriter_active] ; Check if text is currently active + and a + jr z, .exit + + ; Delay writing a letter by 10 frames + ld a, [textwriter_curdelay] + inc a + ld [textwriter_curdelay], a + cp 20 + jr c, .exit + + ; Reset the curdelay + ld a, 0 + ld [textwriter_curdelay], a + + ; Get the memory address pointing to the text currently being + ; displayed. This is stored in textwriter_curtext + ld hl, textwriter_curtext ; Get the memory address of the pointer + ld d, [hl] ; Store the first byte address to d + inc hl ; Move to next byte + ld e, [hl] ; Store the next byte address to e + ; We now have the current letter being drawn at [de] + + ld hl, _SCRN0 ; Load the top-left map position to our map + ld a, [textwriter_posy] + ld c, a + ld a, [textwriter_posx] + ld b, a +.moveyaxis + ; Our first objective is to position the text to correct position + ; We do so shifting the position by $20 for each y axis + ; and position by $01 for each x axis that needs movement + ld a, c ; Load the y position to our accumilator + and a ; Check if it's zero + jr z, .movexaxis ; Jump to x axis if it's zero and no moving necessary + ld a, l ; Load the first half of the destination position + add a, $20 ; Add $20 (one vertical line) + ld l, a ; Store the position back + ld a, h ; Grab the second part of the address + adc a, $00 ; Add only the accumilator + ld h, a ; Store the position back + dec c ; Decrement our y parameter + jr .moveyaxis ; Repeat +.movexaxis + ; Now we check and move the position based on the x position + ld a, b ; Load the x position to our accimulator + and a ; Check if it's zero + jr z, .drawletter ; Jump to drawletter if it's zero already + inc hl ; Increment the map position by one since we need to move by x position + dec b ; Decrement our x parameter + jr .movexaxis ; Repeat +.drawletter + ld a, [de] ; Load the first byte in the string + ld bc, textwriter_map + ld c, a + ld a, [bc] + ld [hl], a ; Print the character byte directly onto the map + + ld a, [de] + and a ; Check if we reached the end + jr nz, .continue + ; We have reached the end of the string, disable textwriter + ld a, 0 + ld [textwriter_active], a +.continue + ; Time to update our work ram + ; Load the position pointer of current text to next letter + ld hl, textwriter_curtext + inc de ; Increment to next letter in string + ld a, d + ld [hli], a + ld a, e + ld [hl], a + ; Update the x position in ram + ld a, [textwriter_posx] + inc a + ld [textwriter_posx], a + ; Update the current letter being drawn in ram + ld a, [textwriter_curletter] + inc a + ld [textwriter_curletter], a + +.exit + ld a, [textwriter_active] + ret + + +; Print String on screen +; @param b - PosX on where to draw text +; @param c - PosY on where to draw text +; @param de - Address source string +; @return a +; @return b +; @return c +; @return hl - Destination memory in map +; @return hl = Address Target to the byte after the last byte on the target (garbage) +PrintString: + ld hl, _SCRN0 ; Load the top-left map position to our map +.moveyaxis2 + ; Our first objective is to position the text to correct position + ; We do so shifting the position by $20 for each y axis + ; and position by $01 for each x axis that needs movement + ld a, c ; Load the y position to our accumilator + and a ; Check if it's zero + jr z, .movexaxis2 ; Jump to x axis if it's zero and no moving necessary + ld a, l ; Load the first half of the destination position + add a, $20 ; Add $20 (one vertical line) + ld l, a ; Store the position back + ld a, b ; Grab the second part of the address + adc a, $00 ; Add only the accumilator + ld b, a ; Store the position back + dec c ; Decrement our y parameter + jr .moveyaxis2 ; Repeat +.movexaxis2 + ; Now we check and move the position based on the x position + ld a, b ; Load the x position to our accimulator + and a ; Check if it's zero + jr z, .start2 ; Jump to start2 if it's zero already + inc hl ; Increment the map position by one since we need to move by x position + dec b ; Decrement our x parameter + jr .movexaxis2 ; Repeat +.start2 + ld a, [de] ; Load the first byte in the string + ld [hl], a ; Write the byte from the accumilator to the destination address + ; Also increment destination by one. + ; This effectively prints the character byte in accumilator + inc de ; Increment to next byte in string + inc hl + and a ; And together a with itself. This sets the flag + jr nz, .start2 ; Go back to start if we didn't just copy a zero. + ret + + diff --git a/main.asm b/main.asm index 4f4967e..cab609e 100644 --- a/main.asm +++ b/main.asm @@ -2,10 +2,21 @@ INCLUDE "hardware.inc" INCLUDE "game/game.asm" INCLUDE "tools.asm" +SECTION "InterruptVblank", ROM0[$0040] + reti +SECTION "InterruptLCDC", ROM0[$0048] + reti +SECTION "InterruptTimer_Overflow", ROM0[$0050] + reti +SECTION "InterruptSerial", ROM0[$0058] + reti +SECTION "Interruptp1thru4", ROM0[$0060] + reti + SECTION "Header", ROM0[$100] EntryPoint: ; - di ; Disable interrupts. That way we can avoid dealing with them, especially since we didn't talk about them yet + ei ; Simple nop ; di ; Disable interrupts for now jp Start ; Leave this tiny space REPT $150 - $104 @@ -21,9 +32,9 @@ Start: ; Turn off the LCD .waitVBlank - ld a, [rLY] ; Load the current vertical line being rendered - cp 144 ; Check if we're past VBlank (1 to 144 = carry bit set) - jr c, .waitVBlank ; Jump back to waitVBlank if rLY is less than 144 + ld a, $01 + ld [$FFFF], a ; Force enable VBLANK Interrupt + halt xor a ; ld a, 0 ; Reset the accumilator register ; Our target is to set bit 7 reset on the LDC to turn it off @@ -31,21 +42,11 @@ Start: ; ----------- Font Begin copying to VRAM ----------- ; Start copying our font to vram - ld de, FontTiles ; The start of the font tiles + ld de, Page1 ; The start of the font tiles ld hl, $9000 ; Set registers hl to $9000 which is the beginning of vram - ld bc, FontTilesEnd - FontTiles ; The total length of font tiles + ld bc, Page1End - Page1 ; The total length of font tiles call Memcpy -; ----------- Begin copying string to palette ----------- - ld hl, $9800 ; This will print the string at the top-left corner of screen - ld de, HelloWorldStr -.copyString - ld a, [de] ; Read the first byte in the string to accumilator - ld [hli], a ; Write the byte from the accumilator to the destination while incrementing destination address - inc de ; Move to next byte in string - and a ; AND a with A, Checking if we copied a zero (end of string) - jr nz, .copyString ; Continue copying while not zero - ; ----------- Start the display ----------- ; Init the display register ld a, %11100100 ; Load the palette of white, light gray, dark gray, black @@ -59,17 +60,15 @@ Start: ld [rNR52], a ; Turn off sound ; Turn the screen on, display background - ld a, %10000001 ; screen on = 8th bit, background display = 1st bit + ld a, LCDCF_ON|LCDCF_BGON + ; ld a, %10000001 ; screen on = 8th bit, background display = 1st bit ld [rLCDC], a ; Set the screen flags + ei ; Enable interrupts again? jp GameLoop SECTION "Font", ROM0 -FontTiles: -INCBIN "font.chr" -FontTilesEnd: +Page1: +INCBIN "assets/page1.chr" +Page1End: -Section "Hello wrold string", ROM0 - -HelloWorldStr: - db "Hello World!", 0 diff --git a/main.o b/main.o index 7efa1f3..d1a1e9a 100644 Binary files a/main.o and b/main.o differ diff --git a/test.gb b/test.gb index a073196..4fc280d 100644 Binary files a/test.gb and b/test.gb differ diff --git a/test.map b/test.map new file mode 100644 index 0000000..26c0b98 --- /dev/null +++ b/test.map @@ -0,0 +1,128 @@ +ROM Bank #0 (HOME): + SECTION: $0200-$027F ($0080 bytes) ["ram_textmap"] + $0200 = textwriter_map + SECTION: $0150-$01F5 ($00A6 bytes) ["TextWriter"] + $01D0 = DrawActiveText.exit + $01A2 = DrawActiveText.movexaxis + $0193 = DrawActiveText.moveyaxis + $01AA = DrawActiveText.drawletter + $01BA = DrawActiveText.continue + $01E6 = PrintString.movexaxis2 + $01D7 = PrintString.moveyaxis2 + $01EE = PrintString.start2 + $0150 = StartText + $016C = DrawActiveText + SECTION: $0061-$0074 ($0014 bytes) ["Game Loop"] + $0061 = GameLoop.wait + $006D = GameLoop.mainloop + $0061 = GameLoop + SECTION: $0075-$0085 ($0011 bytes) ["Hello wrold string"] + $0075 = HelloWorldStr + SECTION: $0031-$003C ($000C bytes) ["Tools"] + $003A = WaitVBlank + $0031 = Memcpy + SECTION: $0040-$0040 ($0001 bytes) ["InterruptVblank"] + SECTION: $0048-$0048 ($0001 bytes) ["InterruptLCDC"] + SECTION: $0050-$0050 ($0001 bytes) ["InterruptTimer_Overflow"] + SECTION: $0058-$0058 ($0001 bytes) ["InterruptSerial"] + SECTION: $0060-$0060 ($0001 bytes) ["Interruptp1thru4"] + SECTION: $0100-$014F ($0050 bytes) ["Header"] + SECTION: $0000-$0030 ($0031 bytes) ["Intro code"] + $0000 = Start + $0000 = Start.waitVBlank + SECTION: $0280-$127F ($1000 bytes) ["Font"] + $0280 = Page1 + $1280 = Page1End + SLACK: $2E23 bytes + +WRAM Bank #0: + SECTION: $C000-$C006 ($0007 bytes) ["ram_textwriter"] + $C002 = textwriter_curletter + $C005 = textwriter_curdelay + $C006 = textwriter_active + $C003 = textwriter_posx + $C004 = textwriter_posy + $C000 = textwriter_curtext + SLACK: $0FF9 bytes + +WRAM Bank #1: + EMPTY + +WRAM Bank #2: + EMPTY + +WRAM Bank #3: + EMPTY + +WRAM Bank #4: + EMPTY + +WRAM Bank #5: + EMPTY + +WRAM Bank #6: + EMPTY + +WRAM Bank #7: + EMPTY + +VRAM Bank #0: + EMPTY + +VRAM Bank #1: + EMPTY + +OAM: + EMPTY + +HRAM: + EMPTY + +SRAM Bank #0: + EMPTY + +SRAM Bank #1: + EMPTY + +SRAM Bank #2: + EMPTY + +SRAM Bank #3: + EMPTY + +SRAM Bank #4: + EMPTY + +SRAM Bank #5: + EMPTY + +SRAM Bank #6: + EMPTY + +SRAM Bank #7: + EMPTY + +SRAM Bank #8: + EMPTY + +SRAM Bank #9: + EMPTY + +SRAM Bank #10: + EMPTY + +SRAM Bank #11: + EMPTY + +SRAM Bank #12: + EMPTY + +SRAM Bank #13: + EMPTY + +SRAM Bank #14: + EMPTY + +SRAM Bank #15: + EMPTY + diff --git a/test.sym b/test.sym new file mode 100644 index 0000000..9646501 --- /dev/null +++ b/test.sym @@ -0,0 +1,29 @@ +; File generated by rgblink + +00:0200 textwriter_map +00:01D0 DrawActiveText.exit +00:01A2 DrawActiveText.movexaxis +00:0193 DrawActiveText.moveyaxis +00:01AA DrawActiveText.drawletter +00:01BA DrawActiveText.continue +00:01E6 PrintString.movexaxis2 +00:01D7 PrintString.moveyaxis2 +00:01EE PrintString.start2 +00:0150 StartText +00:016C DrawActiveText +00:0061 GameLoop.wait +00:006D GameLoop.mainloop +00:0061 GameLoop +00:0075 HelloWorldStr +00:003A WaitVBlank +00:0031 Memcpy +00:0000 Start +00:0000 Start.waitVBlank +00:0280 Page1 +00:1280 Page1End +00:C002 textwriter_curletter +00:C005 textwriter_curdelay +00:C006 textwriter_active +00:C003 textwriter_posx +00:C004 textwriter_posy +00:C000 textwriter_curtext diff --git a/tools.asm b/tools.asm index 38a01f9..0a8438b 100644 --- a/tools.asm +++ b/tools.asm @@ -17,3 +17,13 @@ Memcpy: or c ; OR together the second half of total amount with the first half jr nz, Memcpy ; Loop back to Memcpy if not zero ret + + +;; +; Waits for the vblank ISR to increment the count of vertical blanks. +; Will lock up if DI, vblank IRQ off, or LCD off. +; Clobbers A, HL +WaitVBlank: +.wait + halt + ret