NimotsuKun for PV-1000解説

サンプルプログラムの荷物君をz88dkでビルドしましたが仕組みを解説してみます。
>zcc +pv1000 main.c -create-app
上記コマンドでは暗黙的に以下がリンクされています。
main.c
pv1000_crt0.asm
lores.asm
font.asm
16KB ROM(0000-3FFF)のメモリーマップは以下になります。
0000-01FF CRT0
0200-03FF LORES
0400-0FFF FONTS
1000-3FFF MAIN
32KB ROM(0000-7FFF)のメモリーマップは以下になります。
0000-01FF CRT0
0200-03FF LORES
0400-0FFF FONTS
1000-7FFF MAIN
LORES(0a-1f)とFONTS(20-7f)とPCG(e0-ff)は、RGBで合計32バイト使ってキャラクタ定義します。
N/A(8bytes)
R (8bytes)
G (8bytes)
B (8bytes)
VRAMにキャラクタ番号(00-ff)を描くと表示されます。 RAMのメモリーマップは以下になります。
B800-BAFF VRAM
BB00-BBFF RAM
BC00-BFFF PCG
JOYPAD入力やPSG出力はEnri's Home PAGEを参考に、I/Oを叩いたらゲームつくれると思います。 http://www43.tok2.com/home/cmpslv/Pv1000/EnrPV1.htm

NimotsuKun for PV-1000

ゲームプログラマになる前に覚えておきたい技術

http://www.shuwasystem.co.jp/support/7980html/2118.html

f:id:tanam:20190522085507p:plain

サンプルプログラムの荷物君をz88dkでビルドしてPV-1000で動かしてみます。

>zcc +pv1000 main.c -create-app

main.c

/*

  Nimotsu Kun for PV-1000
                               by tanam [2019]

*/

#include "games.h"

typedef int bool;
typedef int Object;
typedef unsigned char u8;
typedef unsigned short u16;
#define true 1
#define false 0
#define KEY_UP1     MOVE_UP
#define KEY_DOWN1   MOVE_DOWN
#define KEY_LEFT1   MOVE_LEFT
#define KEY_RIGHT1  MOVE_RIGHT
#define KEY_A       MOVE_FIRE1
#define KEY_B       MOVE_FIRE2

unsigned char *screen=0xB800;
u16 game[224];

/* #壁 _空間 .ゴール oブロック p人 */
const char* gStageData = 
"\
########\n\
# .. p #\n\
# oo   #\n\
#      #\n\
########\n\
        \n\
        0\
 ###### \n\
#.    # \n\
# o o # \n\
# op# # \n\
#   # # \n\
#.###.# \n\
### ### 0";

#define gStageWidth 8
#define gStageHeight 7

enum OBJ {
	OBJ_SPACE,
	OBJ_WALL,
	OBJ_GOAL,
	OBJ_BLOCK,
	OBJ_BLOCK_ON_GOAL,
	OBJ_MAN,
	OBJ_MAN_ON_GOAL,

	OBJ_UNKNOWN,
};

Object state[56];

void screen_set_char(u8 xts, u8 yts, u8 xte, u8 yte, u16 *map)
{
	int i, j, k;
	k=0;
	for (i = 0; i < 24; i++)
		for (j = 0; j < 32; j++)
			if (j > xts && j < xte && i > yts && i < yte)
				screen[(i+1)*32+j+1]=map[8*(i-1)+j-xts-1];
	return;
}

/* 関数プロトタイプ */
void initialize( Object* state, char* stageData );
void draw( Object* state, int w, int h );
int update( Object* state, u16 input, int w, int h );
bool checkClear( Object* state, int w, int h );

int main(){
	int stage;
 	clg();
	for (stage=0; stage<2; stage++) {
RESET:
		initialize( state, gStageData+stage*63); /* ステージ初期化 */
		/* メインループ */
		while ( true ){
			/* まず描画 */
			draw( state, gStageWidth, gStageHeight );
			/* クリアチェック */
			if ( checkClear(state, gStageWidth, gStageHeight ) ){
				break; /* クリアチェック */
			}
			/* 更新 */
			if (-1 == update( state, joystick(0), gStageWidth, gStageHeight )){
				goto RESET;
			}
		}
	}
	return 0;
}

void initialize( Object* state, char* stageData ){
	const char* d = stageData; /* 読み込みポインタ */
	int x = 0;
	int y = 0;
	while ( *d != '0' ){ /* NULL文字でない間 */
		Object t; /* 特に意味はないが使う回数が多い変数に私は良くtを使う。temporaryの略。たぶんよくない習慣だが、無駄に長い名前にして読みにくいのも困り物だろう。 */
		switch ( *d ){
			case '#': t = OBJ_WALL; break;
			case ' ': t = OBJ_SPACE; break;
			case 'o': t = OBJ_BLOCK; break;
			case 'O': t = OBJ_BLOCK_ON_GOAL; break;
			case '.': t = OBJ_GOAL; break;
			case 'p': t = OBJ_MAN; break;
			case 'P': t = OBJ_MAN_ON_GOAL; break;
			case '\n': x = 0; ++y; t = OBJ_UNKNOWN; break; /* 改行処理 */
			default: t = OBJ_UNKNOWN; break;
		}
		++d;
		if ( t != OBJ_UNKNOWN ){ /* 知らない文字なら無視するのでこのif文がある */
			state[y * gStageWidth + x ] = t; /* 書き込み */
			++x;
		}
	}
	return;
}

void draw(Object* state, int width, int height ){
        int x, y;
 	char font[] = {' ', '#', '.', 'o', 'O', 'p', 'P'};
/*	u16 font[] = {10,11,12,13,14,15,16}; /* Object列挙の順 */
	for ( y = 0; y < height; ++y ){
		for (x=0; x < width; ++x ){
			Object o = state[ y * width + x ];
			game[y * gStageWidth + x] = font[ o ];
		}
	}
	screen_set_char(0, 0, 9, 8, game);
}

/* 第一引数はほかの関数ではstateとしているが、あまりに頻繁に使うので
短いsで済ませている。w,hもそれぞれwidth,heightである。 */
int update( Object* s, u16 input, int w, int h ){
	/* 移動差分に変換(dはdifferenceでもdeltaでもお好きな方の略だと思って欲しい) */
	int dx = 0; 
	int dy = 0;
	int i = -1;
        int x, y, tx, ty, p, tp, tx2, ty2, tp2;
	switch ( input ){
		case KEY_LEFT1: dx = -1; break;
		case KEY_RIGHT1: dx = 1; break;
		case KEY_UP1: dy = -1; break;
		case KEY_DOWN1: dy = 1; break;
		case KEY_A: return -1;
	}
	/* 人座標を検索 */
	for ( i = 0; i < w * h; ++i ){
		if ( s[ i ] == OBJ_MAN || s[ i ] == OBJ_MAN_ON_GOAL ){
			break;
		}
	}
	x = i % w; /* xは幅で割ったあまり */
	y = i / w; /* yは幅で割った商 */

	/* 移動後座標(tに意味はない。ごめんなさい) */
	tx = x + dx;
	ty = y + dy;
	/* 座標の最大最小チェック。外れていれば不許可 */
	if ( tx < 0 || ty < 0 || tx >= w || ty >= h ){
		return;
	}
	/* A.その方向が空白またはゴール。人が移動。 */
	p = y*w + x; /* 人位置 */
	tp = ty*w + tx; /* ターゲット位置(TargetPosition) */
	if ( s[ tp ] == OBJ_SPACE || s[ tp ] == OBJ_GOAL ){
		s[ tp ] = ( s[ tp ] == OBJ_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN; /* ゴールならゴール上の人に */
		s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE; /* もともとゴール上ならゴールに */
	/* B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。 */
	}else if ( s[ tp ] == OBJ_BLOCK || s[ tp ] == OBJ_BLOCK_ON_GOAL ){
		/* 2マス先が範囲内かチェック */
		tx2 = tx + dx;
		ty2 = ty + dy; 
		if ( tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h ){ /* 押せない */
			return;
		}

		tp2 = ( ty + dy )*w + ( tx + dx ); /* 2マス先 */
		if ( s[ tp2 ] == OBJ_SPACE || s[ tp2 ] == OBJ_GOAL ){
			/* 順次入れ替え */
			s[ tp2 ] = ( s[ tp2 ] == OBJ_GOAL ) ? OBJ_BLOCK_ON_GOAL : OBJ_BLOCK;
			s[ tp ] = ( s[ tp ] == OBJ_BLOCK_ON_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN;
			s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE;
		}
	}
	return 0;
}

/* ブロックのみがなければクリアしている。 */
bool checkClear(Object* s, int width, int height ){
        int i;
	for (i = 0; i < width*height; ++i ){
		if ( s[ i ] == OBJ_BLOCK ){
			return false;
		}
	}
	return true;
}

黄金の墓で遊ぼう会

ハッシュタグ(2019/5/26)

ADAMem DOS and ADAMem SDL v1.81

Visual Studio 2008でADAMem SDLをビルドしてみました。
ADAMem DOS and ADAMem SDL v1.81 - Page 2 - ColecoVision / Adam - AtariAge Forums

AdamemSDL.c

	Mouse_Init ();
//	keyboardmode=(EmuMode)? 1:0;
	keyboardmode=0;

Z80IO.h

;
//#if (__GNUC__ <= 3 && __GNUC__MINOR__ <= 4) \
//	|| (__GNUC__ >= 2 && __GNUC__MINOR__ >= 7)
//#define FASTCALL        __attribute__ ((regparm(3)))
//#ifdef INLINE_MEM
//#define INLINE_MEM_GNU
//#endif
//#else
#define FASTCALL
//#endif

SVI-328の拡張スロットにMSXカートリッジをつないでみる

SVI-328の拡張スロットに、MSXカートリッジをつなぐ方法を考えてみました。

f:id:tanam:20190223152451p:plain

以下の回路図から転記してみました。

http://www.hardwarebook.info/Spectravideo_SVI318/328_Expansion_Bus

http://d4.princess.ne.jp/msx/datas/slot.html

       SVI-328       MSX
PIN 1  +5V           PIN45  +5V
PIN 2 /CTRL2        NC
PIN 3 +12V          PIN48  +12V
PIN 4 -12V          PIN50  -12V
PIN 5  /CTRL1        NC
PIN 6  /WAIT         PIN 7  /WAIT
PIN 7  /RST          PIN15 /RST
PIN 8  CLK           PIN42 CLK
PIN 9  A15           PIN18  A15
PIN10  A14           PIN25  A14
PIN11  A13           PIN26  A13
PIN12  A12           PIN23  A12
PIN13  A11           PIN19  A11
PIN14  A10           PIN20  A10
PIN15  A9            PIN17  A9
PIN16 A8            PIN24  A8
PIN17 A7            PIN21  A7
PIN18 A6            PIN22  A6
PIN19 A5            PIN31  A5
PIN20  A4            PIN32  A4
PIN21  A3            PIN29  A3
PIN22  A2            PIN32  A2
PIN23  A1            PIN27  A1
PIN24  A0            PIN28  A0
PIN25  /RFSH         PIN 6 /RFSH
PIN26 /EXCSR        NC
PIN27 /M1           PIN 9 /M1
PIN28 /EXCSW        NC
PIN29 /WR           PIN13  /WR
PIN30  /MREQ         PIN12  /MREQ
PIN31  /IORQ         PIN11  /IORQ
PIN32  /RD           PIN14  /RD
PIN33  D0            PIN34  D0
PIN34  D1            PIN33  D1
PIN35  D2            PIN36  D2
PIN36  D3            PIN35  D3
PIN37  D4            PIN38  D4
PIN38  D5            PIN37  D5
PIN39  D6            PIN40  D6
PIN40  D7            PIN39  D7
PIN41 CSOUND        PIN49  CSOUND
PIN42 /INT          PIN 8  /INT
PIN43 /RAMDIS       NC
PIN44 /ROMDIS       NC
PIN45 /BK32         NC
PIN46  /BK31         NC
PIN47  /BK22         NC
PIN48  /BK21         NC
PIN49  GND           PIN41  GND
PIN50  GND           PIN43  GND