Pues resulta que estoy haciendo un programa que intenta acceder directamente a la memoria del mando para leer la información de los Miis y cuando intento compilar el programa me da el siguiente error en tiempo de enlace. ¿Alguién me puede echar un cable?:
> "make"
miis.c
linking ... Miis.elf
miis.o: In function `main':
miis.c:(.text.main+0x4c): undefined reference to `wiiuse_find'
miis.c:(.text.main+0x60): undefined reference to `wiiuse_connect'
miis.c:(.text.main+0xb0): undefined reference to `wiiuse_poll'
collect2: ld returned 1 exit status
make[1]: *** [/d/wii/codigo/Miis/Miis.elf] Error 1
"make": *** [build] Error 2
> Process Exit Code: 2
> Time Taken: 00:05
El código y el make son los que siguen:
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
endif
include $(DEVKITPPC)/wii_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
CXXFLAGS = $(CFLAGS)
LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
# LIBS := -lwiiuse -lbte -logc -lm
LIBS := -lwiiuse -lbte -logc -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS :=
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
$(sFILES:.s=.o) $(SFILES:.S=.o)
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) \
-I$(LIBOGC_INC)
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
-L$(LIBOGC_LIB)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
#---------------------------------------------------------------------------------
run:
SET WIILOAD=tcp:192.168.1.4
..\util\wiiload $(TARGET).dol "fat:/"
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
#---------------------------------------------------------------------------------
# This rule links in binary data with the .jpg extension
#---------------------------------------------------------------------------------
%.jpg.o : %.jpg
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <wiiuse/wiiuse.h>
#include <wiiuse/wpad.h>
static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
#define MII_DATA_ADDR 0x0fca
#define MII_NAME_LENGTH 10
#define MII_CREATOR_NAME_LENGTH 10
typedef struct
{
// addr: 0x00 & 0x01
u16 invalid:1; // doesn't seem to have any effect?
u16 isGirl:1;
u16 month:4;
u16 day:5;
u16 favColor:4; // 0 - 11 (changing to 1111, along with setting the preceeding bit
// results in a grey shirt, some values over 11 will crash the Wii
// when trying to change the favorite color).
u16 isFavorite:1;
// addr: 0x02 through 0x15
u16 name[MII_NAME_LENGTH];
// addr: 0x16
u8 height; // 0 - 127
// addr: 0x17
u8 weight; // 0 - 127
// addr: 0x18 - 0x1B
u8 miiID1; // Unique Mii identifier. Seems to increment with time. Also can
u8 miiID2; // be used to change colour of Mii Trousers (see 'See Also' links)
u8 miiID3;
u8 miiID4;
// addr: 0x1C through 0x1F
u8 systemID0; // Checksum8 of first 3 bytes of mac addr
u8 systemID1; // mac addr 3rd-to-last byte
u8 systemID2; // mac addr 2nd-to-last byte
u8 systemID3; // mac addr last byte
// addr: 0x20 & 0x21
u16 faceShape:3; // 0 - 7
u16 skinColor:3; // 0 - 5
u16 facialFeature:4; // 0 - 11
u16 unknown1:3; // Mii appears unaffected by changes to this data
u16 mingleOff:1; // 0 = Mingle, 1 = Don't Mingle
u16 unknown2:1; // Mii appears unaffected by changes to this data
u16 downloaded:1; // If the Mii has been downloaded from the Check Mii Out Channel
// addr: 0x22 & 0x23
u16 hairType:7; // 0 - 71, Value is non-sequential with regard to page, row and column
u16 hairColor:3; // 0 - 7
u16 hairPart:1; // 0 = Normal, 1 = Reversed
u16 unknown3:5;
// addr: 0x24 through 0x27
u32 eyebrowType:5; // 0 - 23, Value is non-sequential with regard to page, row and column
u32 unknown4:1;
u32 eyebrowRotation:4; // 0 - 11, Default value varies based on eyebrow type
u32 unknown5:6;
u32 eyebrowColor:3; // 0 - 7
u32 eyebrowSize:4; // 0 - 8, Default = 4
u32 eyebrowVertPos:5; // 3 - 18, Default = 10
u32 eyebrowHorizSpacing:4; // 0 - 12, Default = 2
// addr: 0x28 through 0x2B
u32 eyeType:6; // 0 - 47, Value is non-sequential with regard to page, row and column
u32 unknown6:2;
u32 eyeRotation:3; // 0 - 7, Default value varies based on eye type
u32 eyeVertPos:5; // 0 - 18, Default = 12
u32 eyeColor:3; // 0 - 5
u32 unknown7:1;
u32 eyeSize:3; // 0 - 7, Default = 4
u32 eyeHorizSpacing:4; // 0 - 12, Default = 2
u32 unknown8:5;
// addr: 0x2C & 0x2D
u16 noseType:4; // 0 - 11, Value is non-sequential with regard to row and column
u16 noseSize:4; // 0 - 8, Default = 4
u16 noseVertPos:5; // 0 - 18, Default = 9
u16 unknown9:3;
// addr: 0x2E & 2F
u16 lipType:5; // 0 - 23, Value is non-sequential with regard to page, row and column
u16 lipColor:2; // 0 - 2
u16 lipSize:4; // 0 - 8, Default = 4
u16 lipVertPos:5; // 0 - 18, Default = 13
// addr: 0x30 & 0x31
u16 glassesType:4; // 0 - 8
u16 glassesColor:3; // 0 - 5
u16 unknown:1; // when turned on mii does not appear (use not known)
u16 glassesSize:3; // 0 - 7, Default = 4
u16 glassesVertPos:5; // 0 - 20, Default = 10
// addr: 0x32 & 33
u16 mustacheType:2; // 0 - 3
u16 beardType:2; // 0 - 3
u16 facialHairColor:3; // 0 - 7
u16 mustacheSize:4; // 0 - 8, Default = 4
u16 mustacheVertPos:5; // 0 - 16, Default = 10
// addr: 0x34 & 0x35
u16 moleOn:1; // 0 = No Mole, 1 = Has Mole
u16 moleSize:4; // 0 - 8, Default = 4
u16 moleVertPos:5; // 0 - 30, Default = 20
u16 moleHorizPos:5; // 0 - 16, Default = 2
u16 unknown10:1;
// addr: 0x36 through 0x49
u16 creatorName[MII_CREATOR_NAME_LENGTH];
} MII_DATA_STRUCT;
typedef struct {
u32 RNCD;
u16 MiiParadeSlots;
u32 unknown;
MII_DATA_STRUCT MiiArray[0x0A];
u16 crc;
} MIIDATABLOCK; // 752 bytes en total
/**
* Calculate a modified CRC16-CCITT checksum of a byte array, as used for
* checking the validity of a Mii data block stored on a Wiimote.
*
* @param bytes the byte array to calculate the checksum for
* @return the checksum (in the lower 16 bits)
*/
int crc (u8* bytes[]) {
int crc = 0x0000;
int byteIndex, bitIndex,counter;
for (byteIndex = 0; byteIndex < sizeof(bytes); byteIndex++) {
for (bitIndex = 7; bitIndex >= 0; bitIndex--) {
crc = (((crc << 1) | ((*bytes[byteIndex] >> bitIndex) & 0x1)) ^
(((crc & 0x8000) != 0) ? 0x1021 : 0));
}
}
for (counter = 16; counter > 0; counter--) {
crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
}
return (crc & 0xFFFF);
}
#define MAX_WIIMOTES 4
/**
* @brief Callback that handles an event.
*
* @param wm Pointer to a wiimote_t structure.
*
* This function is called automatically by the wiiuse library when an
* event occurs on the specified wiimote.
*/
void handle_event(struct wiimote_t* wm, int event) {
printf("\n\n--- EVENT [id %i] ---\n", wm->unid);
/* if a button is pressed, report it */
if (IS_PRESSED(wm, WIIMOTE_BUTTON_A)) printf("A pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_B)) printf("B pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_UP)) printf("UP pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_DOWN)) printf("DOWN pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_LEFT)) printf("LEFT pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_RIGHT)) printf("RIGHT pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_MINUS)) printf("MINUS pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_PLUS)) printf("PLUS pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_ONE)) printf("ONE pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_TWO)) printf("TWO pressed\n");
if (IS_PRESSED(wm, WIIMOTE_BUTTON_HOME)) printf("HOME pressed\n");
/*
* Pressing minus will tell the wiimote we are no longer interested in movement.
* This is useful because it saves battery power.
*/
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))
wiiuse_motion_sensing(wm, 0);
/*
* Pressing plus will tell the wiimote we are interested in movement.
*/
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))
wiiuse_motion_sensing(wm, 1);
/*
* Pressing B will toggle the rumble
*
* if B is pressed but is not held, toggle the rumble
*/
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_B))
wiiuse_toggle_rumble(wm);
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_UP))
wiiuse_set_ir(wm, 1);
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_DOWN))
wiiuse_set_ir(wm, 0);
/* if the accelerometer is turned on then print angles */
if (WIIUSE_USING_ACC(wm)) {
printf("wiimote roll = %f [%f]\n", wm->orient.roll, wm->orient.a_roll);
printf("wiimote pitch = %f [%f]\n", wm->orient.pitch, wm->orient.a_pitch);
printf("wiimote yaw = %f\n", wm->orient.yaw);
}
/*
* If IR tracking is enabled then print the coordinates
* on the virtual screen that the wiimote is pointing to.
*
* Also make sure that we see at least 1 dot.
*/
if (WIIUSE_USING_IR(wm)) {
int i = 0;
/* go through each of the 4 possible IR sources */
for (; i < 4; ++i) {
/* check if the source is visible */
if (wm->ir.dot[i].visible)
printf("IR source %i: (%u, %u)\n", i, wm->ir.dot[i].rx, wm->ir.dot[i].ry);
}
printf("IR cursor: (%f, %f)\n", wm->ir.x, wm->ir.y);
}
/* show events specific to supported expansions */
if (wm->exp.type == EXP_NUNCHUK) {
/* nunchuk */
struct nunchuk_t* nc = (nunchuk_t*)&wm->exp.nunchuk;
if (IS_PRESSED(nc, NUNCHUK_BUTTON_C)) printf("Nunchuk: C pressed\n");
if (IS_PRESSED(nc, NUNCHUK_BUTTON_Z)) printf("Nunchuk: Z pressed\n");
printf("nunchuk roll = %f\n", nc->orient.roll);
printf("nunchuk pitch = %f\n", nc->orient.pitch);
printf("nunchuk yaw = %f\n", nc->orient.yaw);
printf("nunchuk joystick angle: %f\n", nc->js.ang);
printf("nunchuk joystick magnitude: %f\n", nc->js.mag);
} else if (wm->exp.type == EXP_CLASSIC) {
/* classic controller */
struct classic_ctrl_t* cc = (classic_ctrl_t*)&wm->exp.classic;
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZL)) printf("Classic: ZL pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_B)) printf("Classic: B pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_Y)) printf("Classic: Y pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_A)) printf("Classic: A pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_X)) printf("Classic: X pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZR)) printf("Classic: ZR pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_LEFT)) printf("Classic: LEFT pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_UP)) printf("Classic: UP pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_RIGHT)) printf("Classic: RIGHT pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_DOWN)) printf("Classic: DOWN pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_L)) printf("Classic: FULL L pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_MINUS)) printf("Classic: MINUS pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_HOME)) printf("Classic: HOME pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_PLUS)) printf("Classic: PLUS pressed\n");
if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_R)) printf("Classic: FULL R pressed\n");
printf("classic L button pressed: %f\n", cc->l_shoulder);
printf("classic R button pressed: %f\n", cc->r_shoulder);
printf("classic left joystick angle: %f\n", cc->ljs.ang);
printf("classic left joystick magnitude: %f\n", cc->ljs.mag);
printf("classic right joystick angle: %f\n", cc->rjs.ang);
printf("classic right joystick magnitude: %f\n", cc->rjs.mag);
} else if (wm->exp.type == EXP_GUITAR_HERO_3) {
/* guitar hero 3 guitar */
struct guitar_hero_3_t* gh3 = (guitar_hero_3_t*)&wm->exp.gh3;
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_STRUM_UP)) printf("Guitar: Strum Up pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_STRUM_DOWN)) printf("Guitar: Strum Down pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_YELLOW)) printf("Guitar: Yellow pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_GREEN)) printf("Guitar: Green pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_BLUE)) printf("Guitar: Blue pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_RED)) printf("Guitar: Red pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_ORANGE)) printf("Guitar: Orange pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_PLUS)) printf("Guitar: Plus pressed\n");
if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_MINUS)) printf("Guitar: Minus pressed\n");
printf("Guitar whammy bar: %f\n", gh3->whammy_bar);
printf("Guitar joystick angle: %f\n", gh3->js.ang);
printf("Guitar joystick magnitude: %f\n", gh3->js.mag);
}
}
void handle_ctrl_status(struct wiimote_t* wm) {
printf("\n\n--- CONTROLLER STATUS [wiimote id %i] ---\n", wm->unid);
printf("attachment: %i\n", wm->exp.type);
printf("speaker: %i\n", WIIUSE_USING_SPEAKER(wm));
printf("ir: %i\n", WIIUSE_USING_IR(wm));
printf("leds: %i %i %i %i\n", WIIUSE_IS_LED_SET(wm, 1), WIIUSE_IS_LED_SET(wm, 2), WIIUSE_IS_LED_SET(wm, 3), WIIUSE_IS_LED_SET(wm, 4));
printf("battery: %i %%\n", wm->battery_level);
}
/**
* @brief Callback that handles a read event.
*
* @param wm Pointer to a wiimote_t structure.
* @param data Pointer to the filled data block.
* @param len Length in bytes of the data block.
*
* This function is called automatically by the wiiuse library when
* the wiimote has returned the full data requested by a previous
* call to wiiuse_read_data().
*
* You can read data on the wiimote, such as Mii data, if
* you know the offset address and the length.
*
* The \a data pointer was specified on the call to wiiuse_read_data().
* At the time of this function being called, it is not safe to deallocate
* this buffer.
*/
void handle_read(struct wiimote_t* wm, u8* data, unsigned short len) {
int i = 0;
printf("\n\n--- DATA READ [wiimote id %i] ---\n", wm->unid);
printf("finished read of size %i\n", len);
for (; i < len; ++i) {
if (!(i%16))
printf("\n");
printf("%x ", data[i]);
}
printf("\n\n");
}
void Init_Video(void);
//---------------------------------------------------------------------------------
int main(int argc, char **argv) {
//---------------------------------------------------------------------------------
wiimote** wiimotes;
int found, connected;
Init_Video();
/*
* Initialize an array of wiimote objects.
*
* The parameter is the number of wiimotes I want to create.
*/
wiimotes = wiiuse_init(MAX_WIIMOTES, handle_event);
found = wiiuse_find(wiimotes, MAX_WIIMOTES, 5);
if (!found) {
printf ("No wiimotes found.");
return 0;
}
connected = wiiuse_connect(wiimotes, MAX_WIIMOTES);
if (connected)
printf("Connected to %i wiimotes (of %i found).\n", connected, found);
else {
printf("Failed to connect to any wiimote.\n");
return 0;
}
while(1) {
if (wiiuse_poll(wiimotes, MAX_WIIMOTES)) {
int i = 0;
for (; i < MAX_WIIMOTES; ++i) {
switch (wiimotes[i]->event) {
case WIIUSE_EVENT:
/* a generic event occured */
// handle_event(wiimotes[i]);
break;
case WIIUSE_STATUS:
/* a status event occured */
handle_ctrl_status(wiimotes[i]);
break;
case WIIUSE_DISCONNECT:
case WIIUSE_UNEXPECTED_DISCONNECT:
/* the wiimote disconnected */
// handle_disconnect(wiimotes[i]);
break;
case WIIUSE_READ_DATA:
/*
* Data we requested to read was returned.
* Take a look at wiimotes[i]->read_req
* for the data.
*/
break;
case WIIUSE_NUNCHUK_INSERTED:
/*
* a nunchuk was inserted
* This is a good place to set any nunchuk specific
* threshold values. By default they are the same
* as the wiimote.
*/
printf("Nunchuk inserted.\n");
break;
case WIIUSE_CLASSIC_CTRL_INSERTED:
printf("Classic controller inserted.\n");
break;
case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
/* some expansion was inserted */
handle_ctrl_status(wiimotes[i]);
printf("Guitar Hero 3 controller inserted.\n");
break;
case WIIUSE_NUNCHUK_REMOVED:
case WIIUSE_CLASSIC_CTRL_REMOVED:
case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
/* some expansion was removed */
handle_ctrl_status(wiimotes[i]);
printf("An expansion was removed.\n");
break;
default:
break;
}
}
}
// Esperamos al siguiente frame
VIDEO_WaitVSync();
}
/*
* Disconnect the wiimotes
*/
wiiuse_cleanup(wiimotes, MAX_WIIMOTES);
return 0;
}
void Init_Video(void) {
// Initialise the video system
VIDEO_Init();
// Obtain the preferred video mode from the system
// This will correspond to the settings in the Wii menu
rmode = VIDEO_GetPreferredMode(NULL);
// Allocate memory for the display in the uncached region
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
// Initialise the console, required for printf
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
// Set up the video registers with the chosen mode
VIDEO_Configure(rmode);
// Tell the video hardware where our display memory is
VIDEO_SetNextFramebuffer(xfb);
// Make the display visible
VIDEO_SetBlack(FALSE);
// Flush the video register changes to the hardware
VIDEO_Flush();
// Wait for Video setup to complete
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
}