Skip to content

Commit e85dfb0

Browse files
author
Ian Romanick
committed
Initial support for Mindscape Music Board and AY-3-8910 family PSGs
1 parent ff28f89 commit e85dfb0

File tree

2 files changed

+339
-1
lines changed

2 files changed

+339
-1
lines changed

README.TXT

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ Optional parameters:
7777
1000RSX was estimated using a generic 25MHz 386sx system
7878
with a PicoGUS.
7979

80+
msmb - Enables AY-3-8910 playback. Sets /psg:300 /psg:302. The
81+
Mindscape Music Board has two AY-3-8913 chips.
8082

8183
If /mode is used in conjunction with a second PSG installed
8284
via add-on board, /psg must also be specified for each

src/main.c

+337-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct vgm_buf {
2929

3030
enum {
3131
sn76489 = 0,
32+
ay8910,
3233
};
3334

3435
static int psg_mode = sn76489;
@@ -387,6 +388,22 @@ psg_off(void)
387388
outp(psg1_io, 0xdf);
388389
outp(psg1_io, 0xff);
389390
}
391+
} else {
392+
outp(psg0_io + 0, 0x08);
393+
outp(psg0_io + 1, 0x00);
394+
outp(psg0_io + 0, 0x09);
395+
outp(psg0_io + 1, 0x00);
396+
outp(psg0_io + 0, 0x0a);
397+
outp(psg0_io + 1, 0x00);
398+
399+
if (psg1_io != 0) {
400+
outp(psg1_io + 0, 0x08);
401+
outp(psg1_io + 1, 0x00);
402+
outp(psg1_io + 0, 0x09);
403+
outp(psg1_io + 1, 0x00);
404+
outp(psg1_io + 0, 0x0a);
405+
outp(psg1_io + 1, 0x00);
406+
}
390407
}
391408
}
392409

@@ -754,6 +771,315 @@ play_Tandy_sound(struct vgm_buf *v, struct vgm_header *header)
754771
return;
755772
}
756773

774+
static void
775+
play_AY8910_sound(struct vgm_buf *v, struct vgm_header *header)
776+
{
777+
bool done = false;
778+
779+
while (!done) {
780+
uint8_t command = get_uint8(v);
781+
782+
switch (command) {
783+
case 0x30: /* reserved one-byte command. */
784+
case 0x31: /* AY8910 stereo mask */
785+
case 0x32: /* reserved one-byte command. */
786+
case 0x33: /* reserved one-byte command. */
787+
case 0x34: /* reserved one-byte command. */
788+
case 0x35: /* reserved one-byte command. */
789+
case 0x36: /* reserved one-byte command. */
790+
case 0x37: /* reserved one-byte command. */
791+
case 0x38: /* reserved one-byte command. */
792+
case 0x39: /* reserved one-byte command. */
793+
case 0x3a: /* reserved one-byte command. */
794+
case 0x3b: /* reserved one-byte command. */
795+
case 0x3c: /* reserved one-byte command. */
796+
case 0x3d: /* reserved one-byte command. */
797+
case 0x3e: /* reserved one-byte command. */
798+
case 0x3f: /* reserved one-byte command. */
799+
case 0x4f: /* Game Gear PSG stereo */
800+
case 0x50: /* SN76489 / SN76496 write */
801+
case 0x94: /* Stop stream */
802+
printf("command = 0x%02x\n", (unsigned) command);
803+
skip_bytes(v, 1);
804+
break;
805+
806+
case 0x40: /* Mikey write */
807+
case 0x41: /* reserved two-byte command. */
808+
case 0x42: /* reserved two-byte command. */
809+
case 0x43: /* reserved two-byte command. */
810+
case 0x44: /* reserved two-byte command. */
811+
case 0x45: /* reserved two-byte command. */
812+
case 0x46: /* reserved two-byte command. */
813+
case 0x47: /* reserved two-byte command. */
814+
case 0x48: /* reserved two-byte command. */
815+
case 0x49: /* reserved two-byte command. */
816+
case 0x4a: /* reserved two-byte command. */
817+
case 0x4b: /* reserved two-byte command. */
818+
case 0x4c: /* reserved two-byte command. */
819+
case 0x4d: /* reserved two-byte command. */
820+
case 0x4e: /* reserved two-byte command. */
821+
case 0x51: /* YM2413 write */
822+
case 0x52: /* YM2612 port 0 write */
823+
case 0x53: /* YM2612 port 1 write */
824+
case 0x54: /* YM2151 write */
825+
case 0x55: /* YM2203 write */
826+
case 0x56: /* YM2608 port 0 write */
827+
case 0x57: /* YM2608 port 1 write */
828+
case 0x58: /* YM2610 port 0 write */
829+
case 0x59: /* YM2610 port 1 write */
830+
case 0x5a: /* YM3812 write */
831+
case 0x5b: /* YM3526 write */
832+
case 0x5c: /* Y8950 write */
833+
case 0x5d: /* YMZ280B write */
834+
case 0x5e: /* YMF262 port 0 write */
835+
case 0x5f: /* YMF262 port 1 write */
836+
case 0xa1: /* YM2413 write (second chip) */
837+
case 0xa2: /* YM2612 port 0 write (second chip) */
838+
case 0xa3: /* YM2612 port 1 write (second chip) */
839+
case 0xa4: /* YM2151 write (second chip) */
840+
case 0xa5: /* YM2203 write (second chip) */
841+
case 0xa6: /* YM2608 port 0 write (second chip) */
842+
case 0xa7: /* YM2608 port 1 write (second chip) */
843+
case 0xa8: /* YM2610 port 0 write (second chip) */
844+
case 0xa9: /* YM2610 port 1 write (second chip) */
845+
case 0xaa: /* YM3812 write (second chip) */
846+
case 0xab: /* YM3526 write (second chip) */
847+
case 0xac: /* Y8950 write (second chip) */
848+
case 0xad: /* YMZ280B write (second chip) */
849+
case 0xae: /* YMF262 port 0 write (second chip) */
850+
case 0xaf: /* YMF262 port 1 write (second chip) */
851+
case 0xb0: /* RF5C68 write */
852+
case 0xb1: /* RF5C164 write */
853+
case 0xb2: /* PWM write */
854+
case 0xb3: /* GameBoy DMG write */
855+
case 0xb4: /* NES APU write */
856+
case 0xb5: /* MultiPCM write */
857+
case 0xb6: /* uPD7759 write */
858+
case 0xb7: /* OKIM6258 write */
859+
case 0xb8: /* OKIM6295 write */
860+
case 0xb9: /* HuC6280 write */
861+
case 0xba: /* K053260 write */
862+
case 0xbb: /* Pokey write */
863+
case 0xbc: /* WonderSwan write */
864+
case 0xbd: /* SAA1099 write */
865+
case 0xbe: /* ES5506 write */
866+
case 0xbf: /* GA20 write */
867+
printf("command = 0x%02x\n", (unsigned) command);
868+
skip_bytes(v, 2);
869+
break;
870+
871+
case 0xc0: /* Sega PCM write */
872+
case 0xc1: /* RF5C68 write */
873+
case 0xc2: /* RF5C164 write */
874+
case 0xc3: /* MultiPCM write */
875+
case 0xc4: /* QSound write */
876+
case 0xc5: /* SCSP write */
877+
case 0xc6: /* WonderSwan write */
878+
case 0xc7: /* VSU write */
879+
case 0xc8: /* X1-010 write */
880+
case 0xc9: /* reserved three-byte command. */
881+
case 0xca: /* reserved three-byte command. */
882+
case 0xcb: /* reserved three-byte command. */
883+
case 0xcc: /* reserved three-byte command. */
884+
case 0xcd: /* reserved three-byte command. */
885+
case 0xce: /* reserved three-byte command. */
886+
case 0xcf: /* reserved three-byte command. */
887+
case 0xd0: /* YMF278B port write */
888+
case 0xd1: /* YMF271 port write */
889+
case 0xd2: /* SCC1 port write */
890+
case 0xd3: /* K054539 write */
891+
case 0xd4: /* C140 write */
892+
case 0xd5: /* ES5503 write */
893+
case 0xd6: /* ES5506 write */
894+
case 0xd7: /* reserved three-byte command. */
895+
case 0xd8: /* reserved three-byte command. */
896+
case 0xd9: /* reserved three-byte command. */
897+
case 0xda: /* reserved three-byte command. */
898+
case 0xdb: /* reserved three-byte command. */
899+
case 0xdc: /* reserved three-byte command. */
900+
case 0xdd: /* reserved three-byte command. */
901+
case 0xde: /* reserved three-byte command. */
902+
case 0xdf: /* reserved three-byte command. */
903+
case 0xe1: /* C352 write */
904+
printf("command = 0x%02x\n", (unsigned) command);
905+
skip_bytes(v, 3);
906+
break;
907+
908+
case 0xe0: /* Seek to offset in PCM data bank. */
909+
case 0xe2: /* reserved four-byte command. */
910+
case 0xe3: /* reserved four-byte command. */
911+
case 0xe4: /* reserved four-byte command. */
912+
case 0xe5: /* reserved four-byte command. */
913+
case 0xe6: /* reserved four-byte command. */
914+
case 0xe7: /* reserved four-byte command. */
915+
case 0xe8: /* reserved four-byte command. */
916+
case 0xe9: /* reserved four-byte command. */
917+
case 0xea: /* reserved four-byte command. */
918+
case 0xeb: /* reserved four-byte command. */
919+
case 0xec: /* reserved four-byte command. */
920+
case 0xed: /* reserved four-byte command. */
921+
case 0xee: /* reserved four-byte command. */
922+
case 0xef: /* reserved four-byte command. */
923+
case 0xf0: /* reserved four-byte command. */
924+
case 0xf1: /* reserved four-byte command. */
925+
case 0xf2: /* reserved four-byte command. */
926+
case 0xf3: /* reserved four-byte command. */
927+
case 0xf4: /* reserved four-byte command. */
928+
case 0xf5: /* reserved four-byte command. */
929+
case 0xf6: /* reserved four-byte command. */
930+
case 0xf7: /* reserved four-byte command. */
931+
case 0xf8: /* reserved four-byte command. */
932+
case 0xf9: /* reserved four-byte command. */
933+
case 0xfa: /* reserved four-byte command. */
934+
case 0xfb: /* reserved four-byte command. */
935+
case 0xfc: /* reserved four-byte command. */
936+
case 0xfd: /* reserved four-byte command. */
937+
case 0xfe: /* reserved four-byte command. */
938+
case 0xff: /* reserved four-byte command. */
939+
case 0x90: /* Setup stream control */
940+
case 0x91: /* Set stream data */
941+
case 0x95: /* Start stream (fast call) */
942+
printf("command = 0x%02x\n", (unsigned) command);
943+
skip_bytes(v, 4);
944+
break;
945+
946+
case 0x92: /* Set stream frequency */
947+
printf("command = 0x%02x\n", (unsigned) command);
948+
skip_bytes(v, 5);
949+
break;
950+
951+
case 0x93: /* Start stream */
952+
printf("command = 0x%02x\n", (unsigned) command);
953+
skip_bytes(v, 10);
954+
break;
955+
956+
case 0x61:
957+
/* Wait n samples. n is 16-bit value. */
958+
wait_44khz(get_uint16(v));
959+
break;
960+
961+
case 0x62:
962+
/* Wait 735 samples */
963+
wait_44khz(735);
964+
break;
965+
966+
case 0x63:
967+
/* Wait 882 samples */
968+
wait_44khz(882);
969+
break;
970+
971+
case 0x66:
972+
/* End of sound data. */
973+
done = true;
974+
break;
975+
976+
case 0x67: {
977+
/* Data block. */
978+
printf("command = 0x%02x\n", (unsigned) command);
979+
980+
/* Should be 0x66, followed by a byte for the data type. */
981+
uint8_t marker = get_uint8(v);
982+
if (marker != 0x66)
983+
goto parse_error;
984+
985+
skip_bytes(v, 1);
986+
987+
/* The next four bytes specify how much data follows. */
988+
skip_bytes(v, get_uint32(v));
989+
break;
990+
}
991+
992+
case 0x68: {
993+
/* PCM RAM write. */
994+
printf("command = 0x%02x\n", (unsigned) command);
995+
996+
/* Should be 0x66, followed by a byte for the chip type, and 12
997+
* bytes of offsets and sizes.
998+
*/
999+
uint8_t marker = get_uint8(v);
1000+
if (marker != 0x66)
1001+
goto parse_error;
1002+
1003+
skip_bytes(v, 13);
1004+
break;
1005+
}
1006+
1007+
case 0x70:
1008+
case 0x71:
1009+
case 0x72:
1010+
case 0x73:
1011+
case 0x74:
1012+
case 0x75:
1013+
case 0x76:
1014+
case 0x77:
1015+
case 0x78:
1016+
case 0x79:
1017+
case 0x7a:
1018+
case 0x7b:
1019+
case 0x7c:
1020+
case 0x7d:
1021+
case 0x7e:
1022+
case 0x7f:
1023+
/* Wait n+1 samples. */
1024+
wait_44khz((command & 0x0f) + 1);
1025+
break;
1026+
1027+
case 0x80:
1028+
case 0x81:
1029+
case 0x82:
1030+
case 0x83:
1031+
case 0x84:
1032+
case 0x85:
1033+
case 0x86:
1034+
case 0x87:
1035+
case 0x88:
1036+
case 0x89:
1037+
case 0x8a:
1038+
case 0x8b:
1039+
case 0x8c:
1040+
case 0x8d:
1041+
case 0x8e:
1042+
case 0x8f:
1043+
printf("command = 0x%02x\n", (unsigned) command);
1044+
/* YM2612 port 0 write from data pointer, then wait. */
1045+
break;
1046+
1047+
case 0xa0: {
1048+
/* AY8910 write */
1049+
uint8_t v1 = get_uint8(v);
1050+
uint8_t v2 = get_uint8(v);
1051+
1052+
/* https://vgmrips.net/wiki/VGM_Specification#Dual_Chip_Support
1053+
* says:
1054+
*
1055+
* "All other chips use bit 7 (0x80) of the first parameter
1056+
* byte to distinguish between the 1st and 2nd chip."
1057+
*/
1058+
if (v1 & 0x80 == 0) {
1059+
outp(psg0_io, v1 & 0x7f);
1060+
outp(psg0_io + 1, v2);
1061+
} else if (psg1_io != 0) {
1062+
outp(psg1_io, v1 & 0x7f);
1063+
outp(psg1_io + 1, v2);
1064+
}
1065+
break;
1066+
}
1067+
1068+
default:
1069+
printf("command = 0x%02x\n", (unsigned) command);
1070+
goto parse_error;
1071+
}
1072+
}
1073+
1074+
psg_off();
1075+
return;
1076+
1077+
parse_error:
1078+
printf("parse error\n");
1079+
psg_off();
1080+
return;
1081+
}
1082+
7571083
static void
7581084
show_help(const char *progname)
7591085
{
@@ -846,6 +1172,11 @@ static const struct known_mode known_modes[] = {
8461172
* generic 25MHz 386sx system with a PicoGUS.
8471173
*/
8481174
{ "tandy1000rsx", 101, 1079, 0x01e0, 0x0000, sn76489 },
1175+
1176+
/* Mindscape Music Board is a dual AY-3-8913 card. The default base IO
1177+
* addresses for the chips are 300h and 302h.
1178+
*/
1179+
{ "msmb", 0, 0, 0x0300, 0x0302, ay8910 },
8491180
};
8501181

8511182
static int
@@ -1141,7 +1472,12 @@ main(int argc, char **argv)
11411472
header.total_samples);
11421473

11431474
uint32_t before = get_tick();
1144-
play_Tandy_sound(&v, &header);
1475+
1476+
if (psg_mode == sn76489) {
1477+
play_Tandy_sound(&v, &header);
1478+
} else {
1479+
play_AY8910_sound(&v, &header);
1480+
}
11451481
uint32_t after = get_tick();
11461482

11471483
uint32_t elapsed_ms = 55ul * (after - before);

0 commit comments

Comments
 (0)