newcoleco Posted July 29, 2013 Share Posted July 29, 2013 Hello everyone, You might remember one of my previous posts about extreme usage of Video RAM (VRAM) as extra RAM for the game and I decided to do a simple test to see if it worths more investigation. The idea is to use a variation of Graphic Mode I ( 1 charset, 32 pairs of colors for the entire charset, and a limited number of sprites) in a way to free up as much consecutive VRAM as possible for other purpose. DAY 1 After an entire day thinking and calculcating, I came up with the following potentialy interesting VRAM is structure. With a very limited sprites usage, a dynamic map of 96x128 tiles can be stored in 12K of the 16K available. Only 14x12 tiles can be shown on screen at once, each tile rendered as 2x2 characters (32x32 pixels). DAY 2 The following ROM file is a demonstration of the scrolling effect based I've imaged for this test. Depending on where the player is on the map and where (s)he is going, the window scroll or not accordingly allowing to reach every tile of the map. The player's movements are always completed to stop on a tile, never partial tiles, which may feel a bit odd the first time you try but it's an important part of the game mechanics I've in mind. day2.zip Quote Link to comment Share on other sites More sharing options...
+Gemintronic Posted July 29, 2013 Share Posted July 29, 2013 In order to give the player an even bigger map can you implement meta tiles? Something like NES games where one element in the level data is equal to 2x2 tiles on the map? Thank you for continuing your Coleco efforts! Quote Link to comment Share on other sites More sharing options...
hardhat Posted July 29, 2013 Share Posted July 29, 2013 Looks promising so far. Quote Link to comment Share on other sites More sharing options...
newcoleco Posted July 29, 2013 Author Share Posted July 29, 2013 In order to give the player an even bigger map can you implement meta tiles? Something like NES games where one element in the level data is equal to 2x2 tiles on the map? Thank you for continuing your Coleco efforts! Short answer : A different rendering strategy has to be considered in order to use 32x32 pixels per tile without using more RAM space or a lot more computations. Long answer : Extending the map with 32x32 pixels per tile is possible of course but it will results into different considerations and computations. By considering using only a single byte for X and a single byte for Y coordinates, the 8 pixels movements on 16x16 pixels tiles on a 96x128 tiles map provide the possibility of 191x255 coordinates for a 16x16 pixels entity. This way bytes for X and Y coordinates are used fully for all entities including the player giving an elegant and optimal RAM usage and assembly coding. My rendering strategy with 16x16 pixels per tile imply to show always the same number of tiles regardless if they are partialy visible or not, and to render all the tiles in RAM before loading them on screen. By using this strategy but for 32x32 pixels per tile instead of 16x16 pixels per tile will imply to show only 7x5 tiles on screen at once in a 200x136 pixels area instead of 14x10 tiles in a 216x152 pixels area. Note : screen resolution is 256x192 pixels. You might argue that we can have also 232x168 pixels area on screen and still use the same rendering strategy but in that case the size of the buffer in RAM will be 768 bytes long which is unacceptable considering that only 1K of RAM is available for everything including sound table, various informations for the player and other entities, etc. So, it's possible to use more pixels per tile but it's a different case that needs a different approach. Quote Link to comment Share on other sites More sharing options...
PkK Posted July 30, 2013 Share Posted July 30, 2013 Speaking of giant maps, I made a techdemo for what was intended to become part of a T&T game years ago: http://atariage.com/forums/topic/118543-colecovision-tt-rpg-very-early-techdemo/ No VDP magic there, just procedural map generation. The map size is 32768 x 32768 tiles, tile size is 8 x 8 pixels. I wish I was a productive as Daniel, and would be able to complete games more often; but I just don't find much time for ColecoVision programming these days. At least prgramming in C instead of asm helps a lot. Philipp Quote Link to comment Share on other sites More sharing options...
+nanochess Posted July 30, 2013 Share Posted July 30, 2013 Zombie Near has a map of 132x66 tiles of 16x16 pixels (264x132 characters) It was not so easy to compress it in 32K, but I've used Huffman coding for each room (12x6) and each room is uncompressed as player visits it. Besides for each room there are 4 bits to signal dynamic modifications to room (no key, no scientist, no life) And the ROM contains three maps inside Quote Link to comment Share on other sites More sharing options...
F-Cycles Posted July 30, 2013 Share Posted July 30, 2013 There is always a trade off between : complexity of the map, algorithm complexity, size of memory and processing time. The map usually has to be not only good looking, but interested in it's feature to offer a good game play. The algorithm written to compress the data should tend to stay simple but take the advantage of the feature of the map. As a rule of thumb for optimization, I use the one which said... to first look to what is the "biggest" (for compression) or "slower" (for processing time). I like the fractal approach where tiny stuff (pixels) form bigger objects (tiles) to form even bigger objects (meta-tiles), to form bigger objects (screen), to form.. [a couple of iteration later....] the Universe! Otherwise, I like the idea of using the Video RAM for something else than storing video... so, storing uncompressed data is cool! Quote Link to comment Share on other sites More sharing options...
Pixelboy Posted July 30, 2013 Share Posted July 30, 2013 Otherwise, I like the idea of using the Video RAM for something else than storing video... so, storing uncompressed data is cool! But slow where VRAM I/O is concerned. Quote Link to comment Share on other sites More sharing options...
F-Cycles Posted July 30, 2013 Share Posted July 30, 2013 But slow where VRAM I/O is concerned. Yup, that make it even more challenging... I think it's mostly the Read which are slower than the Write (as the read need to query the byte and return it to the video chip, which place it to the bus for the cpu). While the write, goes only in one direction. Quote Link to comment Share on other sites More sharing options...
newcoleco Posted July 30, 2013 Author Share Posted July 30, 2013 There is no magical answer about making giant maps for Coleco games except that it is possible to find ways to accomplish it. All the ideas are pretty good, giving some solutions to think about in order to answer this question "how to make huge maps for ColecoVision games?" ... which is not easy to answer unless you involve special cartridges that can hold even more ROM and/or RAM. Using bigger tiles per byte is a clever idea to get a bigger map. Unfortunately, it forces the game to display only predetermined parts of rooms and formations of blocks. This method can be used for scrolling games without worrying too much about rendering speed. Using compressed data instead of uncompressed involve to keep track of where to look at in the compressed data to decompress it right on screen... something that you have to be very clever or don't care about cpu time to be used in a game as if it's uncompressed. This idea is pretty good for non-scrolling games but not really for scrolling ones. Quote Link to comment Share on other sites More sharing options...
newcoleco Posted July 31, 2013 Author Share Posted July 31, 2013 (edited) DAY 3 After coding some more to actually get data from the 12K VRAM to be rendered on screen as tiles, I did this quite simple "doodle" program where you can move around and press FIRE#1 to fill up a square and press FIRE#2 to empty one. Note : if you press both, you'll simply empty the square like if only FIRE #2 is pressed. If I wanted to do this doodle software I would have choosen only 1 bit (not an entire byte) to represent each tile, expending by 8 the size of the map... but this is just a test before the real game project starts. Now it's time for me to consider coding the game itself I've in mind which involves some collision detections, some more tiles and entities of course, and so on. I hope you enjoy this thread as much I do! day3.zip And the source code for the programmers interested into Z80 coding and also into how I coded this ROM file. ; gridtest.s .module gridtest ; INTERNAL VARIABLES .globl _player .globl window ; INTERNAL FUNCTIONS .globl _init_gamegraphics .globl _update_window .globl _output_window .globl _tile0 .globl _tile1 ;; TABLE OF VARIABLES (IN RAM) .area _DATA _player:: .ds 2 ; player (Y,X); window:: .ds 2 ; window (Y,X); playerspr_tmp:: .ds 2 ; playerspr_tmp (Y,X); buffered_tiles:: .ds 560 ; RAM buffer ( 28x20 = 560 ); .area _CODE _init_gamegraphics: ld bc, #0x0000 call #0x1fd9 ld bc, #0x0182 call #0x1fd9 ld bc, #0x0202 ; SCREEN * 0400 = 0800 call #0x1fd9 ld bc, #0x032d ; COLOR * 0040 = 0B40 call #0x1fd9 ld bc, #0x0400 ; PATTERN * 0800 = 0000 call #0x1fd9 ld bc, #0x0516 ; SPRITE_ATTRIB * 0080 = 0B00 call #0x1fd9 ld bc, #0x0601 ; SPR_GEN * 0800 = 0800, but only 0B60-0FFF is usable call #0x1fd9 ld bc, #0x0701 call #0x1fd9 ;; CLEAR VRAM ld hl, #0x0000 ; START xor a ; BYTE TO COPY ld de, #0x4000 ; COUNT call #0x1f82 ; FILL VRAM ;; SPRITE PATTERN #0x6C : FILLED SQUARE ld hl, #0x0B60 ; START ld a, #0xff ; BYTE TO COPY ld de, #0x0020 ; COUNT call #0x1f82 ; FILL VRAM ;; INIT SPRITES ATTRIBUTES : (Y,X,ID,COLOR) ld de, #0x0B00 ld hl,#spr_init ld bc,#5 call #0x1fdf ;; GAME CHARSET ld de, #0x0000 ld hl,#game_charset ld bc,#6*8 call #0x1fdf ;; GAME COLOR ld de, #0x0B40 ld hl,#game_color ld bc,#1 call #0x1fdf ;; SCREEN ON ld bc, #0x01e2 call #0x1fd9 ret _tile0: ld a,#0 jp set_tile _tile1: ld a,#1 set_tile: push af ld c,#0xbf ; 10 lines, port #BF ld hl,(_player) ; HL = window position srl h srl h ld a,l rra ld l,a ; HL = offset on map ld de,#0x1000 add hl,de ; HL = offset in VRAM out (c),l ld a,#0x40 add a,h out (c),a dec c pop af out (c),a ret _update_window: call #window_newxy ;; GET MAP DATA BASED ON WINDOW COORDINATES ld bc,#0x0abf ; 10 lines, port #BF ld hl,(window) ; HL = window position srl h srl h ld a,l rra ld l,a ; HL = offset on map ld de,#0x1000 add hl,de ; HL = offset in VRAM ex de,hl ; DE = offset in VRAM ld hl,#buffered_tiles+560-140 ; HL = offset in RAM jp $4 $3: ld a,e add #0x80 ld e,a adc a,d sub e ld d,a $4: out (c),e nop out (c),d push bc ld bc,#0xebe ; 14 bytes, port #BE inir pop bc djnz $3 ;;;; ld de,#buffered_tiles ; DE = POINTER TO BUFFER FOR TILES TO BE ON SCREEN ld hl,#buffered_tiles+560-140 ; HL = POINTER TO PARTIAL MAP IN RAM ld b,#10 ; B = 10 lines $5: push bc ld b,#14 ; B = 14 tiles per line $6: push bc xor a ld c,(hl) inc hl push hl sla c rla sla c rla ld b,a ld hl,#game_tiles add hl,bc ld bc,#0x0002 ldir push de push hl ld hl,#0x001a add hl,de ex de,hl pop hl ld c,#2 ldir pop de pop hl pop bc djnz $6 ex de,hl ld c,#0x1c add hl,bc ex de,hl pop bc djnz $5 ret _output_window: ld hl,#buffered_tiles ;; ADJUST WINDOW OFFSET xor a ld de,(window) bit 0,d jr z,$0 add #28 $0: bit 0,e jr z,$1 add #1 $1: ld e,a ld d,#0 add hl,de ;; OUTPUT TO VRAM ld de,#0x0863 ld b,#19 $2: push bc ld bc,#27 push de call #0x1fdf pop de inc hl push hl ld hl,#0x0020 add hl,de ex de,hl pop hl pop bc djnz $2 ;; PLAYER SPRITE ld hl,(_player) ld de,(window) ld a,h sub d sla a sla a sla a add #23 ld (playerspr_tmp),a ld a,l sub e sla a sla a sla a add #24 ld (playerspr_tmp+1),a ld de, #0x0B00 ld hl,#playerspr_tmp ld bc,#2 call #0x1fdf ret window_newxy: ld hl,(_player) ld de,(window) ld a,l or a sub #8 jr nc, $11 xor a $11: sub #246-8 jr c, $12 xor a $12: add #246-8 ld l,a or a sub #17-8 jr nc, $13 xor a $13: cp e jr c, $14 ld e,a $14: ld a,l or a cp e jr nc, $15 ld e,a $15: ld a,h or a sub #6 jr nc, $17 xor a $17: sub #184-6 jr c, $18 xor a $18: add #184-6 ld h,a or a sub #11-6 jr nc, $19 xor a $19: cp d jr c,$20 ld d,a $20: ld a,h or a cp d jr nc, $21 ld d,a $21: ld (window),de ret spr_init: .db #0xD0,#0x00,#0x6C,#0x0E,#0xD0 game_charset: .db #0x00,#0x00,#0x00,#0x00,#0x00,#0x00,#0x00,#0x00 .db #0xFF,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80 .db #0xFF,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01 .db #0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0x80,#0xFF .db #0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0x01,#0xFF .db #0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF,#0xFF game_color: .db #0x51 game_tiles: .db #0x01,#0x02,#0x03,#0x04 .db #0x05,#0x05,#0x05,#0x05 #include <coleco.h> #include <getput1.h> extern void init_gamegraphics(); extern void update_window(); extern void output_window(); extern void tile0(); extern void tile1(); struct coord { byte x; byte y; }; extern struct coord player; char dy; char dx; void nmi(void) {} void update_player(void) { if (dy!=0 || dx!=0) { player.y+=dy; dy = 0; player.x+=dx; dx = 0; } else { if (joypad_1&FIRE1) tile1(); if (joypad_1&FIRE2) tile0(); if (player.x>0 && joypad_1&LEFT) dx = -1; if (player.x<254 && joypad_1&RIGHT) dx = 1; if (player.y>0 && joypad_1&UP) dy = -1; if (player.y<190 && joypad_1&DOWN) dy = 1; player.y+=dy; player.x+=dx; } } void main(void) { init_gamegraphics(); dy = 0; dx = 0; loop: delay(1); update_player(); update_window(); delay(1); output_window(); goto loop; } Edited July 31, 2013 by newcoleco 1 Quote Link to comment Share on other sites More sharing options...
F-Cycles Posted July 31, 2013 Share Posted July 31, 2013 DAY 3 After coding some more to actually get data from the 12K VRAM to be rendered on screen as tiles, I did this quite simple "doodle" program where you can move around and press FIRE#1 to fill up a square and press FIRE#2 to empty one. Note : if you press both, you'll simply empty the square like if only FIRE #2 is pressed. Look like it could become a Level Editor feature for a game... Quote Link to comment Share on other sites More sharing options...
newcoleco Posted July 31, 2013 Author Share Posted July 31, 2013 Look like it could become a Level Editor feature for a game... Very good observation/suggestion! 1 Quote Link to comment Share on other sites More sharing options...
hardhat Posted August 2, 2013 Share Posted August 2, 2013 (edited) Looking good. For "Reckless Wizard" I use 24x24 pixel tiles, and I have 16x8 (128 in total) screens with 8x8 tiles. So that is 8k of maps uncompressed. Since I have about 40 tile types I use the top two bits to specify the run length (from 1 to 4) and the bottom 6 bits to specify the tile type. So RLE but not the conventional RLE compression. It works pretty good, with a 8k->3.2k compression. And it is super easy to decompress. So no pattern compression but easy stuff to decompress. To make it a changable map, every tile has an "alternate tile". Then in video memory, there is 8 bytes (64 bits) to say if the main tile or the backup tile are active for each tile on that screen. It doesn't take much to read 8 bytes or write 8 bytes. I don't see how writing to video ram is any different in speed than reading, unless you mean read-update-write is slow. Edited August 2, 2013 by hardhat Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.