As promised, code time!
First off, the repo for the custom FAT library is here:
https://github.com/Sorunome/GB_FatI have straight up a little optimization question about the library, as you can see here
https://github.com/Sorunome/GB_Fat/blob ... #L127-L128 I need a uint8_t buf[2]; only to be able to generate a uint16_t, is there some way to implicitally cast a uint16_t to a uint8_t[2] ?
Anyhow, now on to the demo! It consists of two files:
main.ino:
- Code: Select all
#include <SPI.h>
#include <Gamebuino.h>
#include <EEPROM.h>
#include <GB_Fat.h>
Gamebuino gb;
#define TILEMAP_WIDTH 12
#define TILEMAP_HEIGHT 8
byte camX = 0;
byte camY = 0;
byte sprites[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
byte sprite_player[] = {0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55};
byte tilemap[TILEMAP_WIDTH * TILEMAP_HEIGHT];
GB_Fat sd;
GB_File file;
#include "graphics.h"
class Player {
byte x=8,y=8;
public:
void update(){
if(gb.buttons.repeat(BTN_RIGHT,0)){
x++;
}
if(gb.buttons.repeat(BTN_LEFT,0)){
x--;
}
if(gb.buttons.repeat(BTN_UP,0)){
y--;
}
if(gb.buttons.repeat(BTN_DOWN,0)){
y++;
}
sprite_xor(sprite_player,x,y);
moveCam(x - (LCDWIDTH / 2), y - (LCDHEIGHT / 2));
}
};
void setup() {
// put your setup code here, to run once:
gb.begin();
gb.setFrameRate(40);
gb.display.clear();
gb.display.println(F("loading card..."));
gb.display.update();
if(sd.init(gb.display.getBuffer(),SPISPEED_VERYHIGH)!=NO_ERROR){
gb.display.clear();
gb.display.print(F("SD card not found."));
gb.display.update();
while(1);
}
gb.display.clear();
gb.display.println(F("seaching for file card..."));
gb.display.update();
file = sd.open("DATA.DAT",gb.display.getBuffer());
if(!file.exists()){
gb.display.clear();
gb.display.print(F("Couldn't open file."));
gb.display.update();
while(1);
}
gb.display.clear();
gb.display.println(F("card found"));
gb.display.update();
file.read(tilemap,0,96);
/*
byte buffer[11];
buffer[10] = '\0';
file = sd.open("TEST.TXT",gb.display.getBuffer());
if(!file.exists()){
gb.display.clear();
gb.display.print(F("Couldn't open file."));
gb.display.update();
while(1);
}
file.read(buffer,32768 - 5,10);
gb.display.clear();
gb.display.println(reinterpret_cast<const char*>(buffer));
gb.display.update();
while(1);*/
}
Player player;
void loop() {
// put your main code here, to run repeatedly:
if(gb.update()){
drawTilemap();
player.update();
}
}
graphics.h:
- Code: Select all
void sprite_xor(byte data[], byte xx, byte yy){
int8_t x = xx - camX;
int8_t y = yy - camY;
uint8_t* buf = (((y+8)&0xF8)>>1) * 21 + x + gb.display.getBuffer();
asm volatile(
"mov R20,%[y]\n\t"
"ldi R17,7\n\t"
"add R20,R17\n\t"
"brmi End\n\t"
"cpi %[y],48\n\t"
"brpl End\n\t"
"inc R20\n\t"
"ldi R16,8\n\t"
"andi R20,7\n\t"
"cpi R20,0\n\t"
"breq LoopAligned\n"
"LoopStart:\n\t"
"tst %[x]\n\t"
"brmi LoopSkip\n\t"
"cpi %[x],84\n\t"
"brcc LoopSkip\n\t"
"ld R17,Z\n\t"
"eor R18,R18\n\t"
"mov R19,R20\n\t"
"clc\n\t"
"LoopShift:\n\t" // carry is still reset from the cpi instruction or from the dec
"rol R17\n\t"
"rol R18\n\t"
"dec R19\n\t"
"brne LoopShift\n\t"
"tst %[y]\n\t"
"brmi LoopSkipPart\n\t"
"ld R19,X\n\t"
"eor R19,R17\n\t"
"st X,R19\n\t"
"LoopSkipPart:\n\t"
"cpi %[y],40\n\t"
"brpl LoopSkip\n\t"
"ld R19,Y\n\t"
"eor R19,R18\n\t"
"st Y,R19\n\t"
"LoopSkip:\n\t"
"eor R18,R18\n\t"
"ldi R19,1\n\t"
"add R26,R19\n\t" // INC DOESN'T CHANGE CARRY!
"adc R27,R18\n\t"
"add R28,R19\n\t"
"adc R29,R18\n\t"
"add R30,R19\n\t"
"adc R31,R18\n\t"
"inc %[x]\n\t"
"dec R16\n\t"
"brne LoopStart\n\t"
"rjmp End\n"
"LoopAligned:\n\t"
"tst %[x]\n\t"
"brmi LoopAlignSkip\n\t"
"cpi %[x],84\n\t"
"brcc LoopAlignSkip\n\t"
"ld R17,Z\n\t"
"ld R18,X\n\t"
"eor R18,R17\n\t"
"st X,R18\n\t"
"LoopAlignSkip:\n\t"
"ldi R18,1\n\t"
"add R26,R18\n\t"
"adc R27,R20\n\t"
"add R30,R18\n\t"
"adc R31,R20\n\t"
"inc %[x]\n\t"
"dec R16\n\t"
"brne LoopAligned\n"
"End:\n\t"
::"x" (buf - 84),"y" (buf),"z" (data),[y] "r" (y),[x] "r" (x):"r16","r17","r18","r19","r20");
}
void drawTilemap(){
int8_t startDdx = (-camX) / 8;
int8_t startDdy = (-camY) / 8;
int8_t maxDdx = (LCDWIDTH + 8 - 1 + camX) / 8;
int8_t maxDdy = (LCDHEIGHT + 8 - 1 + camY) / 8;
if(TILEMAP_WIDTH < maxDdx){
maxDdx = TILEMAP_WIDTH;
}
if(TILEMAP_HEIGHT < maxDdy){
maxDdy = TILEMAP_HEIGHT;
}
if(startDdx < 0){
startDdx = 0;
}
if(startDdy < 0){
startDdy = 0;
}
for(byte ddy = startDdy;ddy < maxDdy;ddy++){
for(byte ddx = startDdx;ddx < maxDdx;ddx++){
sprite_xor(sprites + tilemap[ddy*TILEMAP_WIDTH + ddx]*8,ddx*8,ddy*8);
}
}
}
void moveCam(int8_t x,int8_t y){
if(x < 0){
camX = 0;
}else if(x > (TILEMAP_WIDTH*8) - LCDWIDTH){
camX = (TILEMAP_WIDTH*8) - LCDWIDTH;
}else{
camX = x;
}
if(y < 0){
camY = 0;
}else if(y > (TILEMAP_HEIGHT*8) - LCDHEIGHT){
camY = (TILEMAP_HEIGHT*8) - LCDHEIGHT;
}else{
camY = y;
}
}
As always, suggestions are welcome
You will find the required DATA.DAT file attached.
So, how does this thing work?
Including is quite obvious: #include <GB_Fat.h>
Next up you will need an sd handler, GB_Fat sd; This sd handler will be used to "open" files and to initialize.
The filehandler (GB_File file;) is ideally one per open file you need, the sd handler will create it.
Well, before you use the sd card you will need to initialize it: sd.init(gb.display.getBuffer(),SPISPEED_VERYHIGH)
Note how i set here gb.display.getBuffer() as a parameter, as already mentioned it uses external buffers, the init function requires a 512 bytes buffer. I just used SPISPEED_VERYHIGH because why not
IDK really how much of a difference it makes, that is one of the parts copypasted from tinyFAT
The library will not make any checks later on for if you actually initialized the sd handler or not, so be sure to do so!Next up we have file opening! file = sd.open("DATA.DAT",gb.display.getBuffer()); This again requires a 512 bytes buffer, ideal for the screen buffer.
Now is the key part: file.read(tilemap,0,96); first parameter is the buffer, as you can tell I didn't use the screen buffer so this line will not corrupt it! Next is the offset in bytes, and last is the size. So this will read 96 bytes starting from byte 0 into tilemap.
The library does not make checks if the end of the file was reached, due to how it works the buffer will end up with garbage if read beyond the file. However the library won't ever write any more than
size bytes to the buffer! Yay!
Little tricks:
Due to how SD cards work you have the greatest peformance if you align your data to 512-byte blocks, that shouldn't make a big difference, though.
However, what would make a bigger difference is once your filesize hits more than 32768 bytes, as the library will internally need to perform an extra read.
DISCLAIMER: As this is something very low level I just wanne say that I am not responsible for any damage this would do to your gamebuino, even though I am positive that it won't do anything.
I already found it randomly when flashing that the screen wouldn't turn on, however once it turned on after flashing it stayed no matter how often I resetted the gamebuino, so I'll blame USB flashing for now. If it doesn't turn on I managed to get it back by flashing the LOADER.HEX (by holding down c while turning on) reptatitivly and re-inserting the card while turned on.