diff --git a/README.md b/README.md index f5c4b56..304c132 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ collection. 4. **Disc Colors**: - 10 different stone colors to choose from +5. **Input controls** + - Keyboard + - Joystick + - Mouse + ## Othello Rules Othello, also known as Reversi, is a two-player strategy board game played on an diff --git a/assets/sound/corridors_of_time.zsm b/assets/sound/corridors_of_time.zsm deleted file mode 100644 index 74bbf89..0000000 Binary files a/assets/sound/corridors_of_time.zsm and /dev/null differ diff --git a/assets/sound/othello.zsm b/assets/sound/othello.zsm new file mode 100644 index 0000000..5d494bd Binary files /dev/null and b/assets/sound/othello.zsm differ diff --git a/assets/sound/othello_main.fur b/assets/sound/othello_main.fur new file mode 100644 index 0000000..e48cd9e Binary files /dev/null and b/assets/sound/othello_main.fur differ diff --git a/assets/sound/pachelbel_canon_in_d.fur b/assets/sound/pachelbel_canon_in_d.fur deleted file mode 100644 index e8074ac..0000000 Binary files a/assets/sound/pachelbel_canon_in_d.fur and /dev/null differ diff --git a/assets/sound/tile.zsm b/assets/sound/tile.zsm new file mode 100644 index 0000000..0c7d830 Binary files /dev/null and b/assets/sound/tile.zsm differ diff --git a/assets/tiles/tiles.png b/assets/tiles/tiles.png index 3074994..602d72d 100644 Binary files a/assets/tiles/tiles.png and b/assets/tiles/tiles.png differ diff --git a/assets/tiles/tiles.pyxel b/assets/tiles/tiles.pyxel index 45d9edf..42c15d4 100644 Binary files a/assets/tiles/tiles.pyxel and b/assets/tiles/tiles.pyxel differ diff --git a/img/cx16-othello.gif b/img/cx16-othello.gif index e9f6ad3..8d6ff57 100644 Binary files a/img/cx16-othello.gif and b/img/cx16-othello.gif differ diff --git a/src/.gitignore b/src/.gitignore index 3d8486a..5887053 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -4,3 +4,4 @@ *.o *.sym *.map +dump.bin diff --git a/src/Makefile b/src/Makefile index 1eefd8b..660fa2b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ PYTHON=python3 make: OTHELLO.PRG -OTHELLO.PRG: *.c *.h FONTMAP.DAT TILES.DAT COT.ZSM +OTHELLO.PRG: *.c *.h *.s FONTMAP.DAT TILES.DAT OTHELLO.ZSM TILE.ZSM $(CC) -O -o OTHELLO.PRG -t cx16 -C othello.cfg *.c *.s *.lib TILES.DAT: @@ -13,8 +13,11 @@ TILES.DAT: FONTMAP.DAT: $(PYTHON) ../assets/scripts/fontmap.py FONTMAP.DAT -COT.ZSM: - cp -v ../assets/sound/corridors_of_time.zsm COT.ZSM +OTHELLO.ZSM: + cp -v ../assets/sound/othello.zsm OTHELLO.ZSM + +TILE.ZSM: + cp -v ../assets/sound/tile.zsm TILE.ZSM run: OTHELLO.PRG $(EMU) -prg OTHELLO.PRG -run diff --git a/src/constants.h b/src/constants.h index 033c298..c5a9716 100644 --- a/src/constants.h +++ b/src/constants.h @@ -42,6 +42,7 @@ #define TILE_EMPTY_CURSOR 0x02 #define TILE_BLACK 0x03 #define TILE_WHITE 0x04 +#define TILE_MOUSE_CURSOR 0x0F #define TILE_BLACK_CURSOR 0x13 #define TILE_WHITE_CURSOR 0x14 @@ -59,6 +60,10 @@ #define TILE_BEDGB 0x08 #define TILE_BEDGBR 0x09 +// sprites +#define SPRITE_MOUSE_CURSOR 0x00 +#define SPRITE_TILE_CURSOR 0x65 + // stone colors #define STONE_RED 0x03 #define STONE_TEAL 0x04 @@ -102,4 +107,7 @@ #define RAMBANK (*(uint8_t*)0) +#define SCROLL_BACKGROUND 1 +#define SCROLLSPEED 50 + #endif // _CONSTANTS_H \ No newline at end of file diff --git a/src/game.c b/src/game.c index 0f385d6..8317e85 100644 --- a/src/game.c +++ b/src/game.c @@ -36,6 +36,7 @@ uint8_t boardsize = 8; uint8_t board_offset_x = 6; uint8_t board_offset_y = 4; uint8_t no_move_counter = 0; +uint16_t cpu_waittime = 200; /** * @brief Initialize the game scene @@ -66,6 +67,14 @@ void init_game() { // reset no move counter no_move_counter = 0; + // set all sprites to transparent + for(i=0; i<(boardsize * boardsize); i++) { + assign_sprite(i+1, 0x00); + } + + // assign cursor sprite + assign_sprite(SPRITE_TILE_CURSOR, TILE_EMPTY_CURSOR); + // build the game board build_playfield(); @@ -87,6 +96,13 @@ void init_game() { // set first player current_player = PLAYER_ONE; + // set CPU wait time + if(player1_type == PLAYER_HUMAN || player2_type == PLAYER_HUMAN) { + cpu_waittime = 500; + } else { + cpu_waittime = 100; + } + // start game gamestate = GAME_RUN; } @@ -100,20 +116,21 @@ void init_game() { */ void set_stone(uint8_t y, uint8_t x, uint8_t stone) { int8_t sx=0, sy=0; + uint8_t tile_idx = y * boardsize + x; + uint8_t sprite_id = tile_idx + 1; // update board and edgefield - board[y * boardsize + x] = stone; - edgefield[y * boardsize + x] = EDGEFIELD_INACTIVE; + board[tile_idx] = stone; + edgefield[tile_idx] = EDGEFIELD_INACTIVE; switch(stone) { case STONE_PLAYER1: - set_tile(y + board_offset_y, x + board_offset_x, stone_color1, PALETTEBYTE); + assign_sprite(sprite_id, stone_color1); + set_sprite(sprite_id, y + board_offset_y, x + board_offset_x); break; case STONE_PLAYER2: - set_tile(y + board_offset_y, x + board_offset_x, stone_color2, PALETTEBYTE); - break; - default: - set_tile(y + board_offset_y, x + board_offset_x, TILE_NONE, PALETTEBYTE); + assign_sprite(sprite_id, stone_color2); + set_sprite(sprite_id, y + board_offset_y, x + board_offset_x); break; } @@ -142,32 +159,11 @@ void set_cursor(uint8_t y, uint8_t x) { uint8_t edge = edgefield[cury * boardsize + curx]; uint32_t map_base_addr = 0x0000; - // remove the old cursor - switch(tile) { - case STONE_PLAYER1: - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color1, PALETTEBYTE); - break; - case STONE_PLAYER2: - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color2, PALETTEBYTE); - break; - } + set_sprite(SPRITE_TILE_CURSOR, board_offset_y + y, board_offset_x + x); // update cursor positions curx = x; cury = y; - - tile = board[y * boardsize + x]; - switch(tile) { - case STONE_PLAYER1: - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color1 + 0x10, PALETTEBYTE); - break; - case STONE_PLAYER2: - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color2 + 0x10, PALETTEBYTE); - break; - default: - set_tile(cury + board_offset_y, curx + board_offset_x, TILE_EMPTY_CURSOR, PALETTEBYTE); - break; - } } /** @@ -179,15 +175,6 @@ void set_cursor(uint8_t y, uint8_t x) { void move_cursor(int8_t y, int8_t x) { uint8_t tile = board[cury * boardsize + curx]; - // clean up old cursor position - if(tile == STONE_NONE) { - set_tile(cury + board_offset_y, curx + board_offset_x, TILE_NONE, PALETTEBYTE); - } else if(tile == STONE_PLAYER1) { - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color1, PALETTEBYTE); - } else if(tile == STONE_PLAYER2) { - set_tile(cury + board_offset_y, curx + board_offset_x, stone_color2, PALETTEBYTE); - } - x += curx; y += cury; @@ -399,11 +386,14 @@ void count_stones(uint8_t end) { * */ void computer_turn() { - uint8_t besty = 0; - uint8_t bestx = 0; + uint8_t besty[100]; + uint8_t bestx[100]; uint8_t bestvalue = 0; - uint8_t i=0, j=0; - unsigned char keycode = 0; + uint8_t i=0, j=0, k=0, v=0; + clock_t start, next; + + // keep track of time + start = clock(); for(j=0; j 0) { + stonecounter[0] += 10; + } + if(stonecounter[boardsize - 1] > 0) { + stonecounter[boardsize - 1] += 10; + } + if(stonecounter[boardsize * boardsize - 1] > 0) { + stonecounter[boardsize * boardsize - 1] += 10; + } + if(stonecounter[boardsize * (boardsize - 1)] > 0) { + stonecounter[boardsize * (boardsize - 1)] += 10; + } + // now loop once more over the playfield and pick the option that yields // the best result for(j=0; j bestvalue) { - bestvalue = stonecounter[j * boardsize + i]; - bestx = i; - besty = j; + v = stonecounter[j * boardsize + i]; + if(v > bestvalue) { // a new best value + bestvalue = v; + k=0; + besty[k] = j; + bestx[k] = i; + k++; + } else if(v == bestvalue) { // another tile with best value + besty[k] = j; + bestx[k] = i; + k++; } } } if(bestvalue != 0x00) { - place_stone(besty, bestx, PROBE_NO, current_player); + // add artificial delay + do { + next = clock(); + update_background_diagonal(); + sound_fill_buffers(); + } + while (((next - start) * 1000 / CLOCKS_PER_SEC) < cpu_waittime); + + // randomly select best option from series of best values, current value + // of k is the number of times a best value is found; if there is only + // one option (k=1), just pick that option and do not run rand() + if(k > 1) { + k = rand() % (k + 1); // select value in range [0, k-1] + } else { + k = 0; + } + + place_stone(besty[k], bestx[k], PROBE_NO, current_player); no_move_counter = 0; } else { // no stone can be placed, so turn over to the other player @@ -449,6 +477,10 @@ void human_turn() { unsigned char joystat1 = 0xFF; unsigned char joystat2 = 0xFF; unsigned char joyconn = 0xFF; + static uint8_t mouse_buttons = 0x00; + uint16_t *mouse_x = (uint16_t *)0x2; + uint16_t *mouse_y = (uint16_t *)0x4; + int8_t ccurx, ccury; // first check whether the human can actually perform a valid move // if not, we have to swap the turn again (and display an error message) @@ -487,6 +519,7 @@ void human_turn() { case KEYCODE_ESCAPE: gamestate = GAME_MENU; free_resources(); + reset_sprites(); return; } @@ -524,6 +557,32 @@ void human_turn() { return; } } + + // read mouse + asm("ldx #2"); + asm("jsr $FF6B"); + asm("sta %v", mouse_buttons); + + // determine tile position based based on cursor position + ccurx = (*mouse_x >> 4) - board_offset_x; + ccury = (*mouse_y >> 4) - board_offset_y; + if(ccurx >= 0 && ccurx < boardsize && ccury >=0 && ccury < boardsize) { + set_cursor(ccury, ccurx); + } + + if(mouse_buttons & 1) { + // wait until mouse button is released + while(mouse_buttons != 0x00) { + asm("ldx #2"); + asm("jsr $FF6B"); + asm("sta %v", mouse_buttons); + + sound_fill_buffers(); + update_background_diagonal(); + } + // place stone in current mouse location + place_stone(cury, curx, PROBE_NO, current_player); + } } /** @@ -575,8 +634,10 @@ void end_game_state(uint8_t black, uint8_t white) { // display winner if(black > white) { write_string("PLAYER 1 WINS!", 14, 0); + set_background(stone_color1 + 0x10); } else if(white > black) { write_string("PLAYER 2 WINS!", 14, 0); + set_background(stone_color2 + 0x10); } else { write_string("IT'S A TIE!", 14, 0); } @@ -591,11 +652,13 @@ void end_game_state(uint8_t black, uint8_t white) { switch(keycode) { case KEYCODE_RETURN: gamestate = GAME_MENU; + reset_sprites(); return; } sound_fill_buffers(); - } + update_background_diagonal(); + } } /** @@ -624,6 +687,7 @@ uint8_t check_valid_move() { * */ void free_resources() { + uint8_t i=0; free(board); free(edgefield); free(stonecounter); @@ -678,5 +742,6 @@ void wait_joystick_release(uint8_t joy_id) { do { read_joystick(joy_id, &joystat1, &joystat2, &joyconn); sound_fill_buffers(); + update_background_diagonal(); } while(joystat1 != 0xFF || joystat2 != 0xFF); } \ No newline at end of file diff --git a/src/game.h b/src/game.h index 925be22..56f3b6f 100644 --- a/src/game.h +++ b/src/game.h @@ -26,10 +26,12 @@ #include #include #include +#include #include "constants.h" #include "video.h" #include "sound.h" +#include "mouse.h" extern uint8_t *board; // store board configuration extern uint8_t *edgefield; // which board positions are 'active' @@ -47,6 +49,7 @@ extern uint8_t boardsize; // board size (6, 8 or 10) extern uint8_t board_offset_x; // board x-offset with respect to screen extern uint8_t board_offset_y; // board y-offset with respect to screen extern uint8_t no_move_counter; // how many turns in a row no move can be made +extern uint16_t cpu_waittime; // time to wait until the computer makes a move /** * @brief Initialize the game scene diff --git a/src/main.c b/src/main.c index ec18878..8eef179 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,8 @@ #include "constants.h" #include "game.h" #include "menu.h" +#include "mouse.h" +#include "sound.h" unsigned char keycode; @@ -37,6 +39,10 @@ void main() { init_sound(); start_bgmusic(); + // enable mouse + init_mouse(); + set_mouse_pointer(TILE_MOUSE_CURSOR); + while(1) { while(gamestate == GAME_MENU) { clear_screen(); @@ -69,6 +75,9 @@ void main() { // update sound buffer sound_fill_buffers(); + + // diagonal background scrolling + update_background_diagonal(); } } } \ No newline at end of file diff --git a/src/menu.c b/src/menu.c index d93e4fc..f63fcc7 100644 --- a/src/menu.c +++ b/src/menu.c @@ -26,25 +26,27 @@ */ void game_menu() { static unsigned char keycode; + unsigned short *mouse_x = (unsigned short *)0x2; + unsigned short *mouse_y = (unsigned short *)0x4; write_string("CX16-OTHELLO", 0, 1); - write_string("RC1", 0, 17); + write_string("music by Crisps", 1, 5); + write_string("1.1.0", 0, 15); - write_string("(1) PLAYER 1:", 2, 1); - write_string("(2) PLAYER 2:", 3, 1); - write_string("(B) BOARD:", 4, 1); + write_string("(1) PLAYER 1:", 3, 1); + write_string("(2) PLAYER 2:", 4, 1); + write_string("(B) BOARD:", 6, 1); + + write_string("(S) SIZE:", 8, 1); write_string("(C) TILE COLOR 1:", 11, 1); write_string("(V) TILE COLOR 2:", 12, 1); - write_string("(S) BOARD", 6, 1); - write_string(" SIZE:", 7, 1); write_string("Hit ENTER to start", 14, 1); print_choice(); while(1) { - // sample keyboard to make adjustments to the game settings asm("jsr $FFE4"); asm("sta %v", keycode); @@ -114,6 +116,7 @@ void game_menu() { // update sound buffer sound_fill_buffers(); + update_background_diagonal(); } } @@ -122,40 +125,46 @@ void game_menu() { * */ void print_choice() { - char buf[20]; + char buf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const uint8_t offsetx = 12; // print whether player 1 is a human or a cpu - write_string(player1_type == PLAYER_HUMAN ? "HUMAN" : "CPU ", 2, 14); + write_string(player1_type == PLAYER_HUMAN ? "HUMAN" : "CPU ", 3, 14); // print whether player 2 is a human or a cpu - write_string(player2_type == PLAYER_HUMAN ? "HUMAN" : "CPU ", 3, 14); + write_string(player2_type == PLAYER_HUMAN ? "HUMAN" : "CPU ", 4, 14); // print board style - write_string(board_type == BOARD_STONE ? "STONE" : "WOOD ", 4, 12); + write_string(board_type == BOARD_STONE ? "STONE" : "WOOD ", 7, 1); // print board size switch(boardsize) { case 6: - memcpy(buf, " 6 x 6 ", 8); + memcpy(buf, "6 x 6 ", 8); break; case 8: - memcpy(buf, " 8 x 8 ", 8); + memcpy(buf, "8 x 8 ", 8); break; case 10: memcpy(buf, "10 x 10", 8); break; } - write_string(buf, 8, 1); + write_string(buf, 9, 1); // build sample board build_board(4, 6, offsetx); + // set sprites + assign_sprite(1, stone_color1); + assign_sprite(2, stone_color2); + assign_sprite(3, stone_color1); + assign_sprite(4, stone_color2); + // print stone colors - set_tile(7, offsetx+1, stone_color1, 0x00); - set_tile(7, offsetx+2, stone_color2, 0x00); - set_tile(8, offsetx+2, stone_color1, 0x00); - set_tile(8, offsetx+1, stone_color2, 0x00); + set_sprite(1, 7, offsetx+1); + set_sprite(2, 7, offsetx+2); + set_sprite(3, 8, offsetx+2); + set_sprite(4, 8, offsetx+1); set_tile(11, 18, stone_color1, 0x00); set_tile(12, 18, stone_color2, 0x00); diff --git a/src/menu.h b/src/menu.h index 7c34891..221d45b 100644 --- a/src/menu.h +++ b/src/menu.h @@ -26,6 +26,8 @@ #include "video.h" #include "game.h" +#include "mouse.h" +#include "sound.h" /** * @brief Show game menu diff --git a/src/mouse.h b/src/mouse.h new file mode 100644 index 0000000..d61592a --- /dev/null +++ b/src/mouse.h @@ -0,0 +1,26 @@ +/************************************************************************** + * * + * Author: Ivo Filot * + * * + * CX16-OTHELLO is free software: * + * you can redistribute it and/or modify it under the terms of the * + * GNU General Public License as published by the Free Software * + * Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * CX16-OTHELLO is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see http://www.gnu.org/licenses/. * + * * + **************************************************************************/ + +#ifndef _MOUSE_H +#define _MOUSE_H + +void __fastcall__ init_mouse(); + +#endif // _MOUSE_H \ No newline at end of file diff --git a/src/mouse.s b/src/mouse.s new file mode 100644 index 0000000..4c392b5 --- /dev/null +++ b/src/mouse.s @@ -0,0 +1,32 @@ +; +; +; Author: Ivo Filot +; +; CX16-OTHELLO is free software: +; you can redistribute it and/or modify it under the terms of the +; GNU General Public License as published by the Free Software +; Foundation, either version 3 of the License, or (at your option) +; any later version. +; +; CX16-OTHELLO is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty +; of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +; See the GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see http://www.gnu.org/licenses/. +; +; + +.include "x16.inc" + +.export _init_mouse + +.code +.proc _init_mouse: near + lda #1 + ldx #40 + ldy #30 + jsr X16::Kernal::MOUSE_CONFIG + rts +.endproc \ No newline at end of file diff --git a/src/othello.cfg b/src/othello.cfg index 374a536..2fdcbf6 100644 --- a/src/othello.cfg +++ b/src/othello.cfg @@ -16,20 +16,20 @@ MEMORY { BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __ONCE_RUN__ - __STACKSIZE__; } SEGMENTS { - ZEROPAGE: load = ZP, type = zp; - EXTZP: load = ZP, type = zp, optional = yes; - LOADADDR: load = LOADADDR, type = ro; - EXEHDR: load = HEADER, type = ro; - STARTUP: load = MAIN, type = ro, optional = yes; - LOWCODE: load = MAIN, type = ro, optional = yes; - CODE: load = MAIN, type = ro; - ZSMKITLIB: load = MAIN, type = ro; - ZSMKITBANK: load = HIRAM, type = bss, define = yes; - RODATA: load = MAIN, type = ro; - DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space - ONCE: load = MAIN, type = ro, define = yes; - BSS: load = BSS, type = bss, define = yes; + ZEROPAGE: load = ZP, type = zp; + EXTZP: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = MAIN, type = ro, optional = yes; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + ZSMKITLIB: load = MAIN, type = ro; + ZSMKITBANK: load = HIRAM, type = bss, define = yes; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space + ONCE: load = MAIN, type = ro, define = yes; + BSS: load = BSS, type = bss, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/src/run.sh b/src/run.sh index 559618d..ef5d2cb 100644 --- a/src/run.sh +++ b/src/run.sh @@ -2,4 +2,4 @@ # # This BASH file is made for MINGW64 BASH # -../../emulator-win/x16emu.exe -prg OTHELLO.PRG -run +../../emulator-win/x16emu.exe -c02 -prg OTHELLO.PRG -run -debug diff --git a/src/sound.s b/src/sound.s index 20ea90b..3f7c1f6 100644 --- a/src/sound.s +++ b/src/sound.s @@ -40,19 +40,27 @@ CHANNEL = 0 ; Start the sound engine ; .proc _init_sound: near - jsr zsmkit::zsm_init_engine ; initialize engine - jsr zsmkit::zsmkit_setisr + jsr zsmkit::zsm_init_engine ; initialize engine + jsr zsmkit::zsmkit_setisr - ldx #0 ; priority - lda #filename ; high byte + ; load background music + ldx #0 ; priority + lda #filename1 ; high byte jsr zsmkit::zsm_setfile - ldx #0 ; priority - lda #$20 ; attenuation value + ; load placement sound for tile + ldx #1 ; priority + lda #filename2 ; high byte + jsr zsmkit::zsm_setfile + + ; set attenuation for background music + ldx #0 ; priority + lda #$20 ; attenuation value jsr zsmkit::zsm_setatten - rts + rts .endproc ; @@ -95,16 +103,12 @@ CHANNEL = 0 ; Operates on fixed channel set by CHANNEL variable ; .proc _play_thumb: near - ldx #TONE1 - jsr play_note - wai - wai - wai - wai - wai - jsr sound_off - rts + ldx #1 + jsr zsmkit::zsm_rewind + clc ; clear carry flag + jsr zsmkit::zsm_setloop + jsr zsmkit::zsm_play + rts ; ; Play single note @@ -147,4 +151,5 @@ sound_off: rts .endproc -filename: .asciiz "cot.zsm" \ No newline at end of file +filename1: .asciiz "othello.zsm" +filename2: .asciiz "tile.zsm" \ No newline at end of file diff --git a/src/video.c b/src/video.c index d55da46..f52f3b1 100644 --- a/src/video.c +++ b/src/video.c @@ -20,6 +20,8 @@ #include "video.h" +clock_t prevtick; + /** * @brief Initialize screen * @@ -38,7 +40,14 @@ void init_screen() { VERA.layer1.tilebase = (TILEBASE >> 11) | 0x03; // 16 x 16 tiles VERA.layer1.mapbase = MAPBASE1 >> 9; - VERA.display.video |= 0b00110000; // enable both layers + // enable both layers + VERA.display.video |= 0b00110000; + + // enable sprites + VERA.display.video |= 0b01000000; + + // set time increment + prevtick = clock(); } /** @@ -48,6 +57,19 @@ void init_screen() { * */ void clear_screen() { + // set background + set_background(TILE_BG); + + // set all foreground tiles to transparent + clear_foreground(); +} + +/** + * @brief Which tile to use for the background + * + * @param tile_id background tile index + */ +void set_background(uint8_t tile_id) { uint8_t i = 0, j=0; uint32_t map_base_addr; @@ -59,13 +81,10 @@ void clear_screen() { for (j=0; j> 16; VERA.address_hi |= 0b10000; @@ -242,28 +261,28 @@ void build_board(uint8_t size, uint8_t offset_y, uint8_t offset_x) { // build board border for (i=0; i SCROLLSPEED) { + prevtick = curtick; + VERA.layer0.hscroll = (VERA.layer0.hscroll - 1) % 16; + VERA.layer0.vscroll = (VERA.layer0.vscroll - 1) % 16; + } + #endif +} + +/** + * @brief Assign a tile to a sprite + * + * @param sprite_id which sprite to place + * @param tile_id which tile to use + */ +void assign_sprite(uint8_t sprite_id, uint8_t tile_id) { + unsigned long sprite_addr = 0x1FC00 + (sprite_id << 3); + uint32_t graph_addr = TILEBASE + (tile_id << 8); + + VERA.address = sprite_addr; + VERA.address_hi = sprite_addr >> 16; + VERA.address_hi |= 0b10000; + + VERA.data0 = graph_addr >> 5; + VERA.data0 = (graph_addr >> 13) | (1 << 7); + VERA.data0 = 16; + VERA.data0 = 0x00; + VERA.data0 = 16; + VERA.data0 = 0x00; + VERA.data0 = 0b00001100; + VERA.data0 = 0b01010000; +} + +/** + * @brief Set the sprite object + * + * @param sprite_id which sprite + * @param posy 16-pixel y-position + * @param posx 16-pixel y-position + */ +void set_sprite(uint8_t sprite_id, uint8_t posy, uint8_t posx) { + unsigned long sprite_addr = 0x1FC00 | (sprite_id << 3) | 2; + + VERA.address = sprite_addr; + VERA.address_hi = sprite_addr >> 16; + VERA.address_hi |= 0b10000; + + VERA.data0 = posx << 4; + VERA.data0 = posx << 12; + VERA.data0 = posy << 4; + VERA.data0 = posy << 12; +} + +/** + * @brief Reset all sprites + * + */ +void reset_sprites() { + uint8_t i; + unsigned long sprite_addr = 0x1FC00; + + VERA.address = sprite_addr; + VERA.address_hi = sprite_addr >> 16; + VERA.address_hi |= 0b10000; + + for(i=0; i<128; i++) { + VERA.data0 = 0x00; + VERA.data0 = 0x00; + VERA.data0 = 16; + VERA.data0 = 0x00; + VERA.data0 = 16; + VERA.data0 = 0x00; + VERA.data0 = 0b00001100; + VERA.data0 = 0b01010000; + } + + set_mouse_pointer(TILE_MOUSE_CURSOR); +} + +/** + * @brief Set the mouse pointer + * + * @param tile_id which tile to use + */ +void set_mouse_pointer(uint8_t tile_id) { + unsigned long sprite_addr = 0x1FC00; + uint32_t graph_addr = TILEBASE + (tile_id << 8); + + VERA.address = sprite_addr; + VERA.address_hi = sprite_addr >> 16; + VERA.address_hi |= 0b10000; + + VERA.data0 = graph_addr >> 5; + VERA.data0 = (graph_addr >> 13) | (1 << 7); } \ No newline at end of file diff --git a/src/video.h b/src/video.h index 3dbd9e3..b6c01b2 100644 --- a/src/video.h +++ b/src/video.h @@ -25,16 +25,26 @@ #include #include #include +#include #include "constants.h" #include "game.h" +extern clock_t prevtick; + /** * @brief Initialize screen * */ void init_screen(); +/** + * @brief Which tile to use for the background + * + * @param tile_id background tile index + */ +void set_background(uint8_t tile_id); + /** * @brief Clears the screen * @@ -112,4 +122,40 @@ void build_board(uint8_t size, uint8_t offset_y, uint8_t offset_x); */ void clear_foreground(); +/** + * @brief Update diagonal background scrolling + * + */ +void update_background_diagonal(); + +/** + * @brief Associate tile to a sprite + * + * @param sprite_id which sprite to place + * @param tile_id which tile to use + */ +void assign_sprite(uint8_t sprite_id, uint8_t tile_id); + +/** + * @brief Position a sprite + * + * @param sprite_id which sprite + * @param posy 16-pixel y-position + * @param posx 16-pixel y-position + */ +void set_sprite(uint8_t sprite_id, uint8_t posy, uint8_t posx); + +/** + * @brief Reset all sprites + * + */ +void reset_sprites(); + +/** + * @brief Set the mouse pointer + * + * @param tile_id which tile to use + */ +void set_mouse_pointer(uint8_t tile_id); + #endif // _VIDEO_H \ No newline at end of file