64using Error = MC::Error;
66constexpr MC::Port kPort =
MEMCARD_PORT == 0 ? MC::Port::Port0 : MC::Port::Port1;
78 void frame()
override;
86unsigned s_failures = 0;
88bool check(
bool pass,
const char *name) {
90 if (!pass) s_failures++;
95bool checkError(
Error got,
Error want,
const char *name) {
97 bool pass = got == want;
98 if (!pass) s_failures++;
99 ramsyscall_printf(
"%s %s: %s", pass ?
"PASS" :
"FAIL", name, MC::errorMessage(got));
105bool same(
const uint8_t *a,
const uint8_t *
b, uint32_t len) {
106 for (uint32_t
i = 0;
i < len;
i++) {
107 if (a[
i] !=
b[
i])
return false;
112bool streq(
const char *a,
const char *
b) {
113 for (uint32_t
i = 0;;
i++) {
114 if (a[
i] !=
b[
i])
return false;
115 if (a[
i] == 0)
return true;
119bool startsWith(
const uint8_t *
s,
const char *prefix) {
120 for (uint32_t
i = 0; prefix[
i];
i++) {
121 if (
s[
i] !=
static_cast<uint8_t
>(prefix[
i]))
return false;
127constexpr const char kPrefix[] =
"BIMEMTEST";
129constexpr const char kTitle[] =
"MT MEMCARD TEST";
130constexpr uint8_t kTitleSjis[] = {0x82, 0x6c, 0x82, 0x73};
132alignas(4) uint8_t s_frame[128];
133alignas(4) uint8_t s_frame2[128];
134constexpr uint32_t kBig = 9000;
135uint8_t s_payload[kBig];
136uint8_t s_readback[kBig];
138void fillFrame(uint8_t *frame, uint32_t seed) {
139 for (uint32_t
i = 0; i < 128; i++) frame[i] = static_cast<uint8_t>(seed * 37 +
i * 11 + (seed >> 8));
142void fillPayload(uint8_t *
buf, uint32_t len, uint32_t seed) {
143 uint32_t x = seed * 2654435761u + 1;
144 for (uint32_t
i = 0;
i < len;
i++) {
145 x = x * 1103515245u + 12345u;
146 buf[
i] =
static_cast<uint8_t
>(x >> 16);
150void buildIcon(FS::Icon &icon, uint8_t frames) {
152 icon.frameCount = frames;
153 for (
unsigned i = 0;
i < 16;
i++) icon.clut[
i] =
static_cast<uint16_t
>(
i * 0x0842);
154 for (
unsigned f = 0; f < 3; f++) {
155 for (
unsigned i = 0;
i < 128;
i++) icon.pixels[f][
i] =
static_cast<uint8_t
>(
i + f * 0x31);
163bool cardIsOursToErase() {
164 Error e = app.m_card.readSectorBlocking(kPort, 0, s_frame);
165 if (
e != Error::OK) {
169 bool zeros =
true, ones =
true;
170 for (
unsigned i = 0;
i < 128;
i++) {
171 zeros = zeros && s_frame[
i] == 0x00;
172 ones = ones && s_frame[
i] == 0xff;
178 if (s_frame[0] ==
'S' && s_frame[1] ==
'C' && same(s_frame + 4, kTitleSjis,
sizeof(kTitleSjis))) {
179 ramsyscall_printf(
"card: frame 0 holds this test's title frame, from an earlier run\n");
182 if (s_frame[0] !=
'M' || s_frame[1] !=
'C') {
186 for (uint16_t
s = 1;
s <= 15;
s++) {
187 e = app.m_card.readSectorBlocking(kPort,
s, s_frame);
188 if (
e != Error::OK) {
192 if (s_frame[0] == 0x51 && !startsWith(s_frame + 0x0a, kPrefix)) {
194 for (
unsigned i = 0; i < 20; i++) name[i] = static_cast<char>(s_frame[0x0a +
i]);
196 ramsyscall_printf(
"REFUSE: block %d holds a save this test did not write: %s\n",
s, name);
205 checkError(app.m_card.readSectorBlocking(kPort, 1024, s_frame), Error::BadSector,
"read sector 1024");
206 checkError(app.m_card.writeSectorBlocking(kPort, 1024, s_frame), Error::BadSector,
"write sector 1024");
210unsigned testCapacity() {
212 for (uint16_t
b = 0;
b < 16;
b++) {
213 fillFrame(s_frame, 0xca00 +
b);
214 Error e = app.m_card.writeSectorBlocking(kPort,
b * 64 + 63, s_frame);
215 if (
e != Error::OK) {
221 for (uint16_t
b = 0;
b < 16;
b++) {
223 Error e = app.m_card.readSectorBlocking(kPort,
b * 64 + 63, s_frame);
224 if (
e != Error::OK) {
225 ramsyscall_printf(
"capacity: tag read from block %d failed: %s\n",
b, MC::errorMessage(
e));
229 for (uint32_t
t = 0;
t < 16;
t++) {
230 fillFrame(s_frame2, 0xca00 +
t);
231 if (same(s_frame, s_frame2, 128)) seen[
b] =
t;
234 bool full =
ok, half =
ok;
235 for (
int b = 0;
b < 16;
b++) {
236 full = full && seen[
b] ==
b;
237 half = half && seen[
b] == (
b < 8 ?
b + 8 :
b);
242 unsigned blocks = full ? 16 : half ? 8 : 0;
244 if (blocks == 8)
ramsyscall_printf(
"capacity: 8 blocks, 64 KiB, block N+8 aliases block N\n");
246 check(blocks == 16,
"capacity probe reads 16 distinct blocks");
248 check(blocks != 0,
"capacity probe reads a 128 KiB or a 64 KiB card");
253void testRawFrames() {
256 static const uint16_t sectors[] = {40, 64, 127, 300, 511};
258 for (
int pass = 0; pass < 2; pass++) {
259 for (uint16_t
s : sectors) {
260 fillFrame(s_frame,
s + pass * 0x500);
261 if (pass) s_frame[0] = 0xff, s_frame[127] = 0x00;
262 Error e = app.m_card.writeSectorBlocking(kPort,
s, s_frame);
263 if (
e != Error::OK) {
268 for (uint16_t
s : sectors) {
269 fillFrame(s_frame,
s + pass * 0x500);
270 if (pass) s_frame[0] = 0xff, s_frame[127] = 0x00;
271 Error e = app.m_card.readSectorBlocking(kPort,
s, s_frame2);
272 if (
e != Error::OK || !same(s_frame, s_frame2, 128)) {
274 e == Error::OK ?
"data differs" :
"no data");
279 check(
ok,
"raw frames: write, read back, overwrite, read back (5 sectors)");
282 volatile bool done =
false;
285 app.m_card.readSector(kPort, 300, s_frame2, [&](
Error e) {
289 while (!
done) app.gpu().pumpCallbacks();
290 fillFrame(s_frame, 300 + 0x500);
291 s_frame[0] = 0xff, s_frame[127] = 0x00;
292 check(
result == Error::OK && same(s_frame, s_frame2, 128),
"raw frames: callback read returns the frame");
295void testFileSystem(
unsigned capacity) {
296 auto &gpu = app.gpu();
298 uint32_t freeBlocks = 0, len = 0,
count = 0;
303 if (!checkError(fs.formatBlocking(gpu, kPort), Error::OK,
"fs: format"))
return;
304 checkError(fs.getCardStateBlocking(gpu, kPort), Error::OK,
"fs: formatted card state");
305 Error e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
307 const uint32_t usable = capacity == 8 ? 7 : 15;
308 check(
e == Error::OK && freeBlocks == usable,
"fs: every usable block free after format");
310 fillPayload(s_payload, 200, 1);
311 checkError(fs.writeFileBlocking(gpu, kPort,
"BIMEMTEST-RW", kTitle, icon, s_payload, 200), Error::OK,
312 "fs: write a one-block file");
313 e = fs.fileExistsBlocking(gpu, kPort,
"BIMEMTEST-RW", &exists);
314 check(
e == Error::OK && exists,
"fs: file exists");
316 e = fs.readFileBlocking(gpu, kPort,
"BIMEMTEST-RW", s_readback, 200, &len);
317 check(
e == Error::OK && len == 200 && same(s_payload, s_readback, 200),
"fs: read back the payload");
319 static FS::FileInfo
info;
320 e = fs.readFileInfoBlocking(gpu, kPort,
"BIMEMTEST-RW", &
info);
322 e == Error::OK && same(
info.title, kTitleSjis,
sizeof(kTitleSjis)) &&
info.icon.frameCount == 2 &&
323 same(
reinterpret_cast<const uint8_t *
>(
info.icon.clut),
reinterpret_cast<const uint8_t *
>(icon.clut), 32) &&
324 same(
info.icon.pixels[0], icon.pixels[0], 128) && same(
info.icon.pixels[1], icon.pixels[1], 128),
325 "fs: title and icon read back");
327 static FS::FileEntry entries[15];
328 e = fs.listFilesBlocking(gpu, kPort, entries, 15, &
count);
329 check(
e == Error::OK &&
count == 1 && streq(entries[0].name,
"BIMEMTEST-RW") && entries[0].sizeInBlocks == 1 &&
330 entries[0].firstBlock == 1,
331 "fs: listing holds the one file, block 1");
334 fillPayload(s_payload, kBig, 2);
335 checkError(fs.writeFileBlocking(gpu, kPort,
"BIMEMTEST-BIG", kTitle, icon, s_payload, kBig), Error::OK,
336 "fs: write a two-block file");
338 e = fs.readFileBlocking(gpu, kPort,
"BIMEMTEST-BIG", s_readback, kBig, &len);
339 check(
e == Error::OK && len == kBig && same(s_payload, s_readback, kBig),
"fs: read back 9000 bytes");
340 e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
341 check(
e == Error::OK && freeBlocks == usable - 3,
"fs: 3 fewer free blocks with 1 + 2 in use");
343 fillPayload(s_payload, 200, 3);
344 checkError(fs.writeFileBlocking(gpu, kPort,
"BIMEMTEST-RW", kTitle, icon, s_payload, 200), Error::OK,
345 "fs: overwrite the one-block file");
346 e = fs.readFileBlocking(gpu, kPort,
"BIMEMTEST-RW", s_readback, 200, &len);
347 check(
e == Error::OK && len == 200 && same(s_payload, s_readback, 200),
"fs: read back the new payload");
348 e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
349 check(
e == Error::OK && freeBlocks == usable - 3,
"fs: overwrite left the free count alone");
351 checkError(fs.writeFileBlocking(gpu, kPort,
"BIMEMTEST-NAME-TOO-LONG", kTitle, icon, s_payload, 1),
352 Error::NameTooLong,
"fs: 23-character name");
353 checkError(fs.writeFileBlocking(gpu, kPort,
"BIMEMTEST-HUGE", kTitle, icon, s_payload, 15 * 8192),
354 Error::FileTooLarge,
"fs: 16-block file");
356 checkError(fs.deleteFileBlocking(gpu, kPort,
"BIMEMTEST-RW"), Error::OK,
"fs: delete the one-block file");
357 e = fs.fileExistsBlocking(gpu, kPort,
"BIMEMTEST-RW", &exists);
358 check(
e == Error::OK && !exists,
"fs: deleted file is gone");
359 checkError(fs.readFileBlocking(gpu, kPort,
"BIMEMTEST-RW", s_readback, 200, &len), Error::FileNotFound,
360 "fs: read a deleted file");
361 checkError(fs.deleteFileBlocking(gpu, kPort,
"BIMEMTEST-RW"), Error::FileNotFound,
"fs: delete it again");
362 fillPayload(s_payload, kBig, 2);
364 e = fs.readFileBlocking(gpu, kPort,
"BIMEMTEST-BIG", s_readback, kBig, &len);
365 check(
e == Error::OK && len == kBig && same(s_payload, s_readback, kBig),
"fs: other file still intact");
366 checkError(fs.deleteFileBlocking(gpu, kPort,
"BIMEMTEST-BIG"), Error::OK,
"fs: delete the two-block file");
367 e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
368 check(
e == Error::OK && freeBlocks == usable,
"fs: every usable block free after both deletes");
373 e = app.m_card.readSectorBlocking(kPort, 0, s_frame);
374 if (
e == Error::OK)
e = app.m_card.writeSectorBlocking(kPort, 512, s_frame);
375 if (
e == Error::OK)
e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
376 check(
e == Error::OK && freeBlocks == 7,
"fs: sector 512 repeating sector 0 reads as 7 usable blocks");
377 unsigned aliasWritten = 0;
378 Error aliasRefused = Error::OK;
379 fillPayload(s_payload, 512, 0x200);
380 for (
unsigned n = 1; n <= 8; n++) {
382 __builtin_memcpy(name,
"BIMEMTEST-A00", 14);
383 name[12] =
static_cast<char>(
'0' + n);
384 aliasRefused = fs.writeFileBlocking(gpu, kPort, name, kTitle, icon, s_payload, 512);
385 if (aliasRefused != Error::OK)
break;
388 check(aliasWritten == 7 && aliasRefused == Error::OutOfSpace,
389 "fs: sector 512 repeating sector 0 takes 7 files, then out of space");
390 checkError(fs.formatBlocking(gpu, kPort), Error::OK,
"fs: format again");
391 e = fs.getFreeBlockCountBlocking(gpu, kPort, &freeBlocks);
392 check(
e == Error::OK && freeBlocks == usable,
"fs: format clears the copy of sector 0 at sector 512");
395bool readsBack(
char names[][21],
unsigned k,
unsigned after) {
397 fillPayload(s_payload, 512, 0x100 + k);
398 Error e = app.m_fs.readFileBlocking(app.gpu(), kPort,
names[k - 1], s_readback, 512, &len);
399 if (
e == Error::OK && len == 512 && same(s_payload, s_readback, 512))
return true;
401 e == Error::OK ?
", wrong data" :
"");
407void testFill(
unsigned capacity) {
408 auto &gpu = app.gpu();
412 static char names[16][21];
413 static FS::FileEntry entries[16];
415 if (!checkError(fs.formatBlocking(gpu, kPort), Error::OK,
"fill: format"))
return;
418 Error refused = Error::OK;
419 for (
unsigned n = 1; n <= 16 && intact; n++) {
420 char *name =
names[n - 1];
421 __builtin_memcpy(name,
"BIMEMTEST-F00", 14);
422 name[11] =
static_cast<char>(
'0' + n / 10);
423 name[12] =
static_cast<char>(
'0' + n % 10);
424 fillPayload(s_payload, 512, 0x100 + n);
425 Error e = fs.writeFileBlocking(gpu, kPort, name, kTitle, icon, s_payload, 512);
426 if (
e != Error::OK) {
434 e = fs.listFilesBlocking(gpu, kPort, entries, 16, &
count);
440 if (!readsBack(
names, n, n)) intact =
false;
443 for (
unsigned k = 1; k <=
written && intact; k++) {
447 check(intact,
"fill: every file written stays listed and readable");
449 bool expected =
written == 15 && refused == Error::OutOfSpace;
450 if (capacity == 8) expected =
written == 7 && refused == Error::OutOfSpace;
452 capacity == 8 ?
"fill: half-size card, 7 files, then out of space" :
"fill: 15 files, then out of space");
458void MemcardTest::prepare() {
464 gpu().initialize(config);
468void MemcardTest::createScene() { pushScene(&scene); }
470void TestScene::frame() {
475 if (
check(app.m_card.probeBlocking(kPort) == Error::OK,
"probe: a card answers") && cardIsOursToErase()) {
477 unsigned capacity = testCapacity();
479 testFileSystem(capacity);
481 checkError(app.m_fs.formatBlocking(app.gpu(), kPort), Error::OK,
"cleanup: format");
486 if (s_failures == 0) {
491 int code = s_failures == 0 ? 0 : 1;
495 register int a0
asm(
"$4") = code;
496 __asm__ volatile(
"break 4, 0\n" : :
"r"(a0) :
"memory");
500int main() {
return app.run(); }
int written
Definition cdda.c:321
int ok
Definition cdlstop.c:75
ramsyscall_printf("T21 phase=%s t=%u v=%02x cause=%u size=%u stat=%02x\n", phase, t/1000, response[0], cause, size,(nopCause==3 &&nopSize >=1) ? stat[0] :0xff)
__builtin_memset(response, 0xff, 16)
uint32_t t
Definition cdltest.c:291
The application class.
Definition application.hh:49
virtual void createScene()
Create the root scene object.
Definition application.hh:86
virtual void prepare()
Prepare the objects for the application.
Definition application.hh:66
A Sony-compatible memory card filesystem.
Definition memory-card-filesystem.hh:77
A low level driver for the PlayStation memory cards.
Definition memory-card.hh:67
Error
The error codes returned by every memory card operation.
Definition memory-card.hh:75
The Scene class.
Definition scene.hh:43
virtual void frame()
Renders a frame.
Definition scene.hh:65
struct s_info info
Definition syscalls.h:535
uint8_t b
Definition gte-depthcue.c:41
int i
Definition gte-regio.c:299
const char * names[]
Definition icache.c:150
#define MEMCARD_PORT
Definition memcard.cpp:57
int main()
Definition memcard.cpp:500
void * result
Definition memcpy.c:52
__asm__(" .section .text.monitorSlotEntry, \"ax\", @progbits\n" " .align 2\n" " .global monitorSlotEntry\n" " .type monitorSlotEntry, @function\n" " .set push\n" " .set noreorder\n" " .set noat\n" "monitorSlotEntry:\n" " mfc0 $v0, $13\n" " nop\n" " andi $v0, $v0, 0x7c\n" " beqz $v0, 1f\n" " srl $v0, $v0, 2\n" " li $v1, " MON_STR(MON_SLOT_EXCMASK) "\n" " srlv $v1, $v1, $v0\n" " andi $v1, $v1, 1\n" " bnez $v1, 2f\n" " nop\n" " jr $ra\n" " nop\n" "1:\n" MON_SLOT_RX_TEST " jr $ra\n" " nop\n" "2:\n" " sw $a0, 0x10($k0)\n" " sw $a1, 0x14($k0)\n" " sw $a2, 0x18($k0)\n" " sw $a3, 0x1c($k0)\n" " mfc0 $a0, $12\n" " nop\n" " sw $a0, 0x8c($k0)\n" " mfc0 $a1, $13\n" " nop\n" " sw $a1, 0x90($k0)\n" " sw $k1, 0x6c($k0)\n" " sw $s0, 0x40($k0)\n" " sw $s1, 0x44($k0)\n" " sw $s2, 0x48($k0)\n" " sw $s3, 0x4c($k0)\n" " sw $s4, 0x50($k0)\n" " sw $s5, 0x54($k0)\n" " sw $s6, 0x58($k0)\n" " sw $s7, 0x5c($k0)\n" " sw $t0, 0x20($k0)\n" " sw $t1, 0x24($k0)\n" " sw $t2, 0x28($k0)\n" " sw $t3, 0x2c($k0)\n" " sw $t4, 0x30($k0)\n" " sw $t5, 0x34($k0)\n" " sw $t6, 0x38($k0)\n" " sw $t7, 0x3c($k0)\n" " sw $t8, 0x60($k0)\n" " sw $t9, 0x64($k0)\n" " sw $gp, 0x70($k0)\n" " sw $sp, 0x74($k0)\n" " sw $fp, 0x78($k0)\n" " mfhi $a0\n" " nop\n" " sw $a0, 0x84($k0)\n" " mflo $a0\n" " nop\n" " sw $a0, 0x88($k0)\n" " move $s0, $ra\n" " la $sp, monitorSlotStack + " MON_STR(MONITOR_SLOT_STACK_WORDS) " * 4 - 16\n" " jal monitorSlotDispatch\n" " nop\n" " lw $k0, 0x108($zero)\n" " move $ra, $s0\n" " lw $k0, 0($k0)\n" " nop\n" " addiu $k0, $k0, 8\n" " lw $a0, 0x84($k0)\n" " lw $a1, 0x88($k0)\n" " mthi $a0\n" " mtlo $a1\n" " lw $a0, 0x10($k0)\n" " lw $a1, 0x14($k0)\n" " lw $a2, 0x18($k0)\n" " lw $a3, 0x1c($k0)\n" " lw $t0, 0x20($k0)\n" " lw $t1, 0x24($k0)\n" " lw $t2, 0x28($k0)\n" " lw $t3, 0x2c($k0)\n" " lw $t4, 0x30($k0)\n" " lw $t5, 0x34($k0)\n" " lw $t6, 0x38($k0)\n" " lw $t7, 0x3c($k0)\n" " lw $t8, 0x60($k0)\n" " lw $t9, 0x64($k0)\n" " lw $s0, 0x40($k0)\n" " lw $sp, 0x74($k0)\n" " jr $ra\n" " nop\n" " .set pop\n" " .size monitorSlotEntry, . - monitorSlotEntry\n" " .previous\n")
check
Definition mkimage.py:54
uint32_t after
Definition readnosetloc.c:139
char * s
Definition string.c:48
char e[1]
Definition string.c:119
Definition configuration.hh:29
Configuration & set(Resolution resolution)
Definition configuration.hh:30
static const void size_t count
Definition syscalls.h:196
static const void * buf
Definition syscalls.h:114
int done
Definition xa.c:238