/*	
 *	cliptia.c
 *
 *	P/ECE TIA (Television Interface Adapter) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Fri Mar 11 03:00:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/****************************************************************************
 *	TIA (Television Interface Adapter)
 ****************************************************************************/

//	[TIA WRITE]
//	
//	$00   VSYNC   0000 00x0  vertical sync set-clear
//	$01   VBLANK  xx00 00x0  vertical blank set-clear
//	$02   WSYNC   ---- ----  wait for leading edge of horizontal blank
//	$03   RSYNC   ---- ----  reset horizontal sync counter
//	$04   NUSIZ0  00xx 0xxx  number-size player-missile 0
//	$05   NUSIZ1  00xx 0xxx  number-size player-missile 1
//	$06   COLUP0  xxxx xxx0  color-lum player 0
//	$07   COLUP1  xxxx xxx0  color-lum player 1
//	$08   COLUPF  xxxx xxx0  color-lum playfield
//	$09   COLUBK  xxxx xxx0  color-lum background
//	$0A   CTRLPF  00xx 0xxx  control playfield ball size & collisions
//	$0B   REFP0   0000 x000  reflect player 0
//	$0C   REFP1   0000 x000  reflect player 1
//	$0D   PF0     xxxx 0000  playfield register byte 0
//	$0E   PF1     xxxx xxxx  playfield register byte 1
//	$0F   PF2     xxxx xxxx  playfield register byte 2
//	$10   RESP0   ---- ----  reset player 0
//	$11   RESP1   ---- ----  reset player 1
//	$12   RESM0   ---- ----  reset missile 0
//	$13   RESM1   ---- ----  reset missile 1
//	$14   RESBL   ---- ----  reset ball
//	$15   AUDC0   0000 xxxx  audio control 0
//	$16   AUDC1   0000 xxxx  audio control 1
//	$17   AUDF0   000x xxxx  audio frequency 0
//	$18   AUDF1   000x xxxx  audio frequency 1
//	$19   AUDV0   0000 xxxx  audio volume 0
//	$1A   AUDV1   0000 xxxx  audio volume 1
//	$1B   GRP0    xxxx xxxx  graphics player 0
//	$1C   GRP1    xxxx xxxx  graphics player 1
//	$1D   ENAM0   0000 00x0  graphics (enable) missile 0
//	$1E   ENAM1   0000 00x0  graphics (enable) missile 1
//	$1F   ENABL   0000 00x0  graphics (enable) ball
//	$20   HMP0    xxxx 0000  horizontal motion player 0
//	$21   HMP1    xxxx 0000  horizontal motion player 1
//	$22   HMM0    xxxx 0000  horizontal motion missile 0
//	$23   HMM1    xxxx 0000  horizontal motion missile 1
//	$24   HMBL    xxxx 0000  horizontal motion ball
//	$25   VDELP0  0000 000x  vertical delay player 0
//	$26   VDELP1  0000 000x  vertical delay player 1
//	$27   VDELBL  0000 000x  vertical delay ball
//	$28   RESMP0  0000 00x0  reset missile 0 to player 0
//	$29   RESMP1  0000 00x0  reset missile 1 to player 1
//	$2A   HMOVE   ---- ----  apply horizontal motion
//	$2B   HMCLR   ---- ----  clear horizontal motion registers
//	$2C   CXCLR   ---- ----  clear collision latches
//	
//	[TIA READ]
//	
//	$0    CXM0P   xx00 0000  read collision M0-P1 M0-P0
//	$1    CXM1P   xx00 0000  read collision M1-P0 M1-P1
//	$2    CXP0FB  xx00 0000  read collision P0-PF P0-BL
//	$3    CXP1FB  xx00 0000  read collision P1-PF P1-BL
//	$4    CXM0FB  xx00 0000  read collision M0-PF M0-BL
//	$5    CXM1FB  xx00 0000  read collision M1-PF M1-BL
//	$6    CXBLPF  x000 0000  read collision BL-PF unused
//	$7    CXPPMM  xx00 0000  read collision P0-P1 M0-M1
//	$8    INPT0   x000 0000  read pot port
//	$9    INPT1   x000 0000  read pot port
//	$A    INPT2   x000 0000  read pot port
//	$B    INPT3   x000 0000  read pot port
//	$C    INPT4   x000 0000  read input
//	$D    INPT5   x000 0000  read input
//	
//	[MOS6532 READ/WRITE] (MOS6532は"RIOT"や"PIA"とも呼ばれます)
//	
//	$280  SWCHA   Port A; input or output (read or write)
//	$281  SWACNT  Port A DDR; 0 = input, 1 = output
//	$282  SWCHB   Port B; console switches (read only)
//	$283  SWBCNT  Port B DDR; hardwired as input
//	$284  INTIM   Timer output (read only)
//	$294  TIM1T   set    1 clock interval (838   nsec/interval)
//	$295  TIM8T   set    8 clock interval (  6.7 usec/interval)
//	$296  TIM64T  set   64 clock interval ( 53.6 usec/interval)
//	$297  T1024T  set 1024 clock interval (858.2 usec/interval)

/****************************************************************************
 *
 ****************************************************************************/

TIA tia; /* TIA core */

#ifdef TIA_GRAYSCALE
const unsigned char tia_grayscale_table[16/*Color*/ * 8/*Luminosity*/] = {
#define TIA_RGB(r,g,b) ((int)((0x##r*0.30 + 0x##g*0.59 + 0x##b*0.11) * 15/255 + 0.5) ^ 15)
#else /*TIA_GRAYSCALE*/
const unsigned char tia_color_table[16/*Color*/ * 8/*Luminosity*/][3/*R,G,B*/] = {
#define TIA_RGB(r,g,b) { 0x##r,0x##g,0x##b }
#endif /*TIA_GRAYSCALE*/
TIA_RGB(00,00,00),TIA_RGB(40,40,40),TIA_RGB(6C,6C,6C),TIA_RGB(90,90,90),TIA_RGB(B0,B0,B0),TIA_RGB(C8,C8,C8),TIA_RGB(DC,DC,DC),TIA_RGB(EC,EC,EC),
TIA_RGB(44,44,00),TIA_RGB(64,64,10),TIA_RGB(84,84,24),TIA_RGB(A0,A0,34),TIA_RGB(B8,B8,40),TIA_RGB(D0,D0,50),TIA_RGB(E8,E8,5C),TIA_RGB(FC,FC,68),
TIA_RGB(70,28,00),TIA_RGB(84,44,14),TIA_RGB(98,5C,28),TIA_RGB(AC,78,3C),TIA_RGB(BC,8C,4C),TIA_RGB(CC,A0,5C),TIA_RGB(DC,B4,68),TIA_RGB(EC,C8,78),
TIA_RGB(84,18,00),TIA_RGB(98,34,18),TIA_RGB(AC,50,30),TIA_RGB(C0,68,48),TIA_RGB(D0,80,5C),TIA_RGB(E0,94,70),TIA_RGB(EC,A8,80),TIA_RGB(FC,BC,94),
TIA_RGB(88,00,00),TIA_RGB(9C,20,20),TIA_RGB(B0,3C,3C),TIA_RGB(C0,58,58),TIA_RGB(D0,70,70),TIA_RGB(E0,88,88),TIA_RGB(EC,A0,A0),TIA_RGB(FC,B4,B4),
TIA_RGB(78,00,5C),TIA_RGB(8C,20,74),TIA_RGB(A0,3C,88),TIA_RGB(B0,58,9C),TIA_RGB(C0,70,B0),TIA_RGB(D0,84,C0),TIA_RGB(DC,9C,D0),TIA_RGB(EC,B0,E0),
TIA_RGB(48,00,78),TIA_RGB(60,20,90),TIA_RGB(78,3C,A4),TIA_RGB(8C,58,B8),TIA_RGB(A0,70,CC),TIA_RGB(B4,84,DC),TIA_RGB(C4,9C,EC),TIA_RGB(D4,B0,FC),
TIA_RGB(14,00,84),TIA_RGB(30,20,98),TIA_RGB(4C,3C,AC),TIA_RGB(68,58,C0),TIA_RGB(7C,70,D0),TIA_RGB(94,88,E0),TIA_RGB(A8,A0,EC),TIA_RGB(BC,B4,FC),
TIA_RGB(00,00,88),TIA_RGB(1C,20,9C),TIA_RGB(38,40,B0),TIA_RGB(50,5C,C0),TIA_RGB(68,74,D0),TIA_RGB(7C,8C,E0),TIA_RGB(90,A4,EC),TIA_RGB(A4,B8,FC),
TIA_RGB(00,18,7C),TIA_RGB(1C,38,90),TIA_RGB(38,54,A8),TIA_RGB(50,70,BC),TIA_RGB(68,88,CC),TIA_RGB(7C,9C,DC),TIA_RGB(90,B4,EC),TIA_RGB(A4,C8,FC),
TIA_RGB(00,2C,5C),TIA_RGB(1C,4C,78),TIA_RGB(38,68,90),TIA_RGB(50,84,AC),TIA_RGB(68,9C,C0),TIA_RGB(7C,B4,D4),TIA_RGB(90,CC,E8),TIA_RGB(A4,E0,FC),
TIA_RGB(00,3C,2C),TIA_RGB(1C,5C,48),TIA_RGB(38,7C,64),TIA_RGB(50,9C,80),TIA_RGB(68,B4,94),TIA_RGB(7C,D0,AC),TIA_RGB(90,E4,C0),TIA_RGB(A4,FC,D4),
TIA_RGB(00,3C,00),TIA_RGB(20,5C,20),TIA_RGB(40,7C,40),TIA_RGB(5C,9C,5C),TIA_RGB(74,B4,74),TIA_RGB(8C,D0,8C),TIA_RGB(A4,E4,A4),TIA_RGB(B8,FC,B8),
TIA_RGB(14,38,00),TIA_RGB(34,5C,1C),TIA_RGB(50,7C,38),TIA_RGB(6C,98,50),TIA_RGB(84,B4,68),TIA_RGB(9C,CC,7C),TIA_RGB(B4,E4,90),TIA_RGB(C8,FC,A4),
TIA_RGB(2C,30,00),TIA_RGB(4C,50,1C),TIA_RGB(68,70,34),TIA_RGB(84,8C,4C),TIA_RGB(9C,A8,64),TIA_RGB(B4,C0,78),TIA_RGB(CC,D4,88),TIA_RGB(E0,EC,9C),
TIA_RGB(44,28,00),TIA_RGB(64,48,18),TIA_RGB(84,68,30),TIA_RGB(A0,84,44),TIA_RGB(B8,9C,58),TIA_RGB(D0,B4,6C),TIA_RGB(E8,CC,7C),TIA_RGB(FC,E0,8C),
#undef TIA_RGB
};

const unsigned char tia_div3_table[256] = {
  0/3,  1/3,  2/3,  3/3,  4/3,  5/3,  6/3,  7/3,  8/3,  9/3, 10/3, 11/3, 12/3, 13/3, 14/3, 15/3,
 16/3, 17/3, 18/3, 19/3, 20/3, 21/3, 22/3, 23/3, 24/3, 25/3, 26/3, 27/3, 28/3, 29/3, 30/3, 31/3,
 32/3, 33/3, 34/3, 35/3, 36/3, 37/3, 38/3, 39/3, 40/3, 41/3, 42/3, 43/3, 44/3, 45/3, 46/3, 47/3,
 48/3, 49/3, 50/3, 51/3, 52/3, 53/3, 54/3, 55/3, 56/3, 57/3, 58/3, 59/3, 60/3, 61/3, 62/3, 63/3,
 64/3, 65/3, 66/3, 67/3, 68/3, 69/3, 70/3, 71/3, 72/3, 73/3, 74/3, 75/3, 76/3, 77/3, 78/3, 79/3,
 80/3, 81/3, 82/3, 83/3, 84/3, 85/3, 86/3, 87/3, 88/3, 89/3, 90/3, 91/3, 92/3, 93/3, 94/3, 95/3,
 96/3, 97/3, 98/3, 99/3,100/3,101/3,102/3,103/3,104/3,105/3,106/3,107/3,108/3,109/3,110/3,111/3,
112/3,113/3,114/3,115/3,116/3,117/3,118/3,119/3,120/3,121/3,122/3,123/3,124/3,125/3,126/3,127/3,
128/3,129/3,130/3,131/3,132/3,133/3,134/3,135/3,136/3,137/3,138/3,139/3,140/3,141/3,142/3,143/3,
144/3,145/3,146/3,147/3,148/3,149/3,150/3,151/3,152/3,153/3,154/3,155/3,156/3,157/3,158/3,159/3,
160/3,161/3,162/3,163/3,164/3,165/3,166/3,167/3,168/3,169/3,170/3,171/3,172/3,173/3,174/3,175/3,
176/3,177/3,178/3,179/3,180/3,181/3,182/3,183/3,184/3,185/3,186/3,187/3,188/3,189/3,190/3,191/3,
192/3,193/3,194/3,195/3,196/3,197/3,198/3,199/3,200/3,201/3,202/3,203/3,204/3,205/3,206/3,207/3,
208/3,209/3,210/3,211/3,212/3,213/3,214/3,215/3,216/3,217/3,218/3,219/3,220/3,221/3,222/3,223/3,
224/3,225/3,226/3,227/3,228/3,229/3,230/3,231/3,232/3,233/3,234/3,235/3,236/3,237/3,238/3,239/3,
240/3,241/3,242/3,243/3,244/3,245/3,246/3,247/3,248/3,249/3,250/3,251/3,252/3,253/3,254/3,255/3,
};

/* Objectの重なりパターン -> 描画色index 変換テーブル */
const unsigned char tia_priority_table[2][1 << 6] = {{
		//      +----------------------- P0
		//      |+---------------------- M0
		//      ||+--------------------- P1
		//      |||+-------------------- M1
		//      ||||+------------------- BL
		//      |||||+------------------ PF
		//      ||||||
		//      |||||| === CTRLPF[2]=0 ===
		//      ||||||  Priority  Objects
		//      |||||| -------------------
		//      ||||||     1       P0,M0
		//      ||||||     2       P1,M1
		//      ||||||     3       BL,PF
		//      ||||||     4       BK
		//      |||||| 
	3,	// $00: 000000 COLUBK
	2,	// $01: 000001 COLUPF
	2,	// $02: 000010 COLUPF
	2,	// $03: 000011 COLUPF
	1,	// $04: 000100 COLUP1
	1,	// $05: 000101 COLUP1
	1,	// $06: 000110 COLUP1
	1,	// $07: 000111 COLUP1
	1,	// $08: 001000 COLUP1
	1,	// $09: 001001 COLUP1
	1,	// $0a: 001010 COLUP1
	1,	// $0b: 001011 COLUP1
	1,	// $0c: 001100 COLUP1
	1,	// $0d: 001101 COLUP1
	1,	// $0e: 001110 COLUP1
	1,	// $0f: 001111 COLUP1
	0,	// $10: 010000 COLUP0
	0,	// $11: 010001 COLUP0
	0,	// $12: 010010 COLUP0
	0,	// $13: 010011 COLUP0
	0,	// $14: 010100 COLUP0
	0,	// $15: 010101 COLUP0
	0,	// $16: 010110 COLUP0
	0,	// $17: 010111 COLUP0
	0,	// $18: 011000 COLUP0
	0,	// $19: 011001 COLUP0
	0,	// $1a: 011010 COLUP0
	0,	// $1b: 011011 COLUP0
	0,	// $1c: 011100 COLUP0
	0,	// $1d: 011101 COLUP0
	0,	// $1e: 011110 COLUP0
	0,	// $1f: 011111 COLUP0
	0,	// $20: 100000 COLUP0
	0,	// $21: 100001 COLUP0
	0,	// $22: 100010 COLUP0
	0,	// $23: 100011 COLUP0
	0,	// $24: 100100 COLUP0
	0,	// $25: 100101 COLUP0
	0,	// $26: 100110 COLUP0
	0,	// $27: 100111 COLUP0
	0,	// $28: 101000 COLUP0
	0,	// $29: 101001 COLUP0
	0,	// $2a: 101010 COLUP0
	0,	// $2b: 101011 COLUP0
	0,	// $2c: 101100 COLUP0
	0,	// $2d: 101101 COLUP0
	0,	// $2e: 101110 COLUP0
	0,	// $2f: 101111 COLUP0
	0,	// $30: 110000 COLUP0
	0,	// $31: 110001 COLUP0
	0,	// $32: 110010 COLUP0
	0,	// $33: 110011 COLUP0
	0,	// $34: 110100 COLUP0
	0,	// $35: 110101 COLUP0
	0,	// $36: 110110 COLUP0
	0,	// $37: 110111 COLUP0
	0,	// $38: 111000 COLUP0
	0,	// $39: 111001 COLUP0
	0,	// $3a: 111010 COLUP0
	0,	// $3b: 111011 COLUP0
	0,	// $3c: 111100 COLUP0
	0,	// $3d: 111101 COLUP0
	0,	// $3e: 111110 COLUP0
	0,	// $3f: 111111 COLUP0
},{
		//      +----------------------- P0
		//      |+---------------------- M0
		//      ||+--------------------- P1
		//      |||+-------------------- M1
		//      ||||+------------------- BL
		//      |||||+------------------ PF
		//      ||||||
		//      |||||| === CTRLPF[2]=1 ===
		//      ||||||  Priority  Objects
		//      |||||| -------------------
		//      ||||||     1       BL,PF
		//      ||||||     2       P0,M0
		//      ||||||     3       P1,M1
		//      ||||||     4       BK
		//      |||||| 
	3,	// $00: 000000 COLUBK
	2,	// $01: 000001 COLUPF
	2,	// $02: 000010 COLUPF
	2,	// $03: 000011 COLUPF
	1,	// $04: 000100 COLUP1
	2,	// $05: 000101 COLUPF
	2,	// $06: 000110 COLUPF
	2,	// $07: 000111 COLUPF
	1,	// $08: 001000 COLUP1
	2,	// $09: 001001 COLUPF
	2,	// $0a: 001010 COLUPF
	2,	// $0b: 001011 COLUPF
	1,	// $0c: 001100 COLUP1
	2,	// $0d: 001101 COLUPF
	2,	// $0e: 001110 COLUPF
	2,	// $0f: 001111 COLUPF
	0,	// $10: 010000 COLUP0
	2,	// $11: 010001 COLUPF
	2,	// $12: 010010 COLUPF
	2,	// $13: 010011 COLUPF
	0,	// $14: 010100 COLUP0
	2,	// $15: 010101 COLUPF
	2,	// $16: 010110 COLUPF
	2,	// $17: 010111 COLUPF
	0,	// $18: 011000 COLUP0
	2,	// $19: 011001 COLUPF
	2,	// $1a: 011010 COLUPF
	2,	// $1b: 011011 COLUPF
	0,	// $1c: 011100 COLUP0
	2,	// $1d: 011101 COLUPF
	2,	// $1e: 011110 COLUPF
	2,	// $1f: 011111 COLUPF
	0,	// $20: 100000 COLUP0
	2,	// $21: 100001 COLUPF
	2,	// $22: 100010 COLUPF
	2,	// $23: 100011 COLUPF
	0,	// $24: 100100 COLUP0
	2,	// $25: 100101 COLUPF
	2,	// $26: 100110 COLUPF
	2,	// $27: 100111 COLUPF
	0,	// $28: 101000 COLUP0
	2,	// $29: 101001 COLUPF
	2,	// $2a: 101010 COLUPF
	2,	// $2b: 101011 COLUPF
	0,	// $2c: 101100 COLUP0
	2,	// $2d: 101101 COLUPF
	2,	// $2e: 101110 COLUPF
	2,	// $2f: 101111 COLUPF
	0,	// $30: 110000 COLUP0
	2,	// $31: 110001 COLUPF
	2,	// $32: 110010 COLUPF
	2,	// $33: 110011 COLUPF
	0,	// $34: 110100 COLUP0
	2,	// $35: 110101 COLUPF
	2,	// $36: 110110 COLUPF
	2,	// $37: 110111 COLUPF
	0,	// $38: 111000 COLUP0
	2,	// $39: 111001 COLUPF
	2,	// $3a: 111010 COLUPF
	2,	// $3b: 111011 COLUPF
	0,	// $3c: 111100 COLUP0
	2,	// $3d: 111101 COLUPF
	2,	// $3e: 111110 COLUPF
	2,	// $3f: 111111 COLUPF
}};

/* Objectの重なりパターン -> Collision 変換テーブル */
const unsigned short tia_collision_table[1 << 6] = {
		//      +----------------------- P0
		//      |+---------------------- M0
		//      ||+--------------------- P1
		//      |||+-------------------- M1
		//      ||||+------------------- BL
		//      |||||+------------------ PF
		//      ||||||
		//      |||||| +---------------- P0-P1  (CXPPMM[7])
		//      |||||| |+--------------- M0-M1  (CXPPMM[6])
		//      |||||| ||+-------------- BL-PF  (CXBLPF[7])
		//      |||||| |||+------------- unused (CXBLPF[6])
		//      |||||| ||||+------------ M1-PF  (CXM1FB[7])
		//      |||||| |||||+----------- M1-BL  (CXM1FB[6])
		//      |||||| ||||||+---------- M0-PF  (CXM0FB[7])
		//      |||||| |||||||+--------- M0-BL  (CXM0FB[6])
		//      |||||| ||||||||+-------- P1-PF  (CXP1FB[7])
		//      |||||| |||||||||+------- P1-BL  (CXP1FB[6])
		//      |||||| ||||||||||+------ P0-PF  (CXP0FB[7])
		//      |||||| |||||||||||+----- P0-BL  (CXP0FB[6])
		//      |||||| ||||||||||||+---- M1-P0  (CXM1P [7])
		//      |||||| |||||||||||||+--- M1-P1  (CXM1P [6])
		//      |||||| ||||||||||||||+-- M0-P1  (CXM0P [7])
		//      |||||| |||||||||||||||+- M0-P0  (CXM0P [6])
		//      |||||| ||||||||||||||||
	0x0000,	// $00: 000000 0000000000000000
	0x0000,	// $01: 000001 0000000000000000
	0x0000,	// $02: 000010 0000000000000000
	0x2000,	// $03: 000011 0010000000000000
	0x0000,	// $04: 000100 0000000000000000
	0x0800,	// $05: 000101 0000100000000000
	0x0400,	// $06: 000110 0000010000000000
	0x2c00,	// $07: 000111 0010110000000000
	0x0000,	// $08: 001000 0000000000000000
	0x0080,	// $09: 001001 0000000010000000
	0x0040,	// $0a: 001010 0000000001000000
	0x20c0,	// $0b: 001011 0010000011000000
	0x0004,	// $0c: 001100 0000000000000100
	0x0884,	// $0d: 001101 0000100010000100
	0x0444,	// $0e: 001110 0000010001000100
	0x2cc4,	// $0f: 001111 0010110011000100
	0x0000,	// $10: 010000 0000000000000000
	0x0200,	// $11: 010001 0000001000000000
	0x0100,	// $12: 010010 0000000100000000
	0x2300,	// $13: 010011 0010001100000000
	0x4000,	// $14: 010100 0100000000000000
	0x4a00,	// $15: 010101 0100101000000000
	0x4500,	// $16: 010110 0100010100000000
	0x6f00,	// $17: 010111 0110111100000000
	0x0002,	// $18: 011000 0000000000000010
	0x0282,	// $19: 011001 0000001010000010
	0x0142,	// $1a: 011010 0000000101000010
	0x23c2,	// $1b: 011011 0010001111000010
	0x4006,	// $1c: 011100 0100000000000110
	0x4a86,	// $1d: 011101 0100101010000110
	0x4546,	// $1e: 011110 0100010101000110
	0x6fc6,	// $1f: 011111 0110111111000110
	0x0000,	// $20: 100000 0000000000000000
	0x0020,	// $21: 100001 0000000000100000
	0x0010,	// $22: 100010 0000000000010000
	0x2030,	// $23: 100011 0010000000110000
	0x0008,	// $24: 100100 0000000000001000
	0x0828,	// $25: 100101 0000100000101000
	0x0418,	// $26: 100110 0000010000011000
	0x2c38,	// $27: 100111 0010110000111000
	0x8000,	// $28: 101000 1000000000000000
	0x80a0,	// $29: 101001 1000000010100000
	0x8050,	// $2a: 101010 1000000001010000
	0xa0f0,	// $2b: 101011 1010000011110000
	0x800c,	// $2c: 101100 1000000000001100
	0x88ac,	// $2d: 101101 1000100010101100
	0x845c,	// $2e: 101110 1000010001011100
	0xacfc,	// $2f: 101111 1010110011111100
	0x0001,	// $30: 110000 0000000000000001
	0x0221,	// $31: 110001 0000001000100001
	0x0111,	// $32: 110010 0000000100010001
	0x2331,	// $33: 110011 0010001100110001
	0x4009,	// $34: 110100 0100000000001001
	0x4a29,	// $35: 110101 0100101000101001
	0x4519,	// $36: 110110 0100010100011001
	0x6f39,	// $37: 110111 0110111100111001
	0x8003,	// $38: 111000 1000000000000011
	0x82a3,	// $39: 111001 1000001010100011
	0x8153,	// $3a: 111010 1000000101010011
	0xa3f3,	// $3b: 111011 1010001111110011
	0xc00f,	// $3c: 111100 1100000000001111
	0xcaaf,	// $3d: 111101 1100101010101111
	0xc55f,	// $3e: 111110 1100010101011111
	0xefff,	// $3f: 111111 1110111111111111
};

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

void
tia_reset(int now, unsigned char vbuff[/*160*192*/])
{
	TIA* p_tia = &tia;

	/* TIA構造体の定義は複雑なので、念のためサイズを確認します。 */
	if(sizeof(TIA) != SIZEOF_TIA) {
		DIE();
	}

	memset(p_tia, 0, sizeof(TIA));

	p_tia->now = now;
	p_tia->vbuff = vbuff;

	p_tia->audio[0].lfsr4 = 1;
	p_tia->audio[0].lfsr5 = 1;
	p_tia->audio[0].lfsr9 = 1;
	p_tia->audio[1].lfsr4 = 1;
	p_tia->audio[1].lfsr5 = 1;
	p_tia->audio[1].lfsr9 = 1;
}

#define SET_REG(REG, MASK, UPD) do {		\
	data &= MASK;				\
	if(p_tia->REG == data) return 0;	\
	if(UPD) tia_partial_update();		\
	p_tia->REG = data;			\
} while(0)
#define H_MOTION(REG, OBJ) do {						\
	int position = p_tia->OBJ.position - ((char)p_tia->REG >> 4);	\
	if(position <    0) position += 160;				\
	if(position >= 160) position -= 160;				\
	p_tia->OBJ.position = position;					\
} while(0)
/* $00   VSYNC   0000 00x0  vertical sync set-clear */
static int tia_write_VSYNC(int data) {
	TIA* p_tia = &tia;
	SET_REG(VSYNC, 0x02, 1);
	return 0;
}
/* $01   VBLANK  xx00 00x0  vertical blank set-clear */
static int tia_write_VBLANK(int data) {
	TIA* p_tia = &tia;
	SET_REG(VBLANK, 0xc2, 1);
	tia_set_i4_i5(p_tia->i4, p_tia->i5); /* I4 I5 latch の変化に追従 */
	return 0;
}
/* $02   WSYNC   ---- ----  wait for leading edge of horizontal blank */
static int tia_write_WSYNC(int data) {
	TIA* p_tia = &tia;
	data = p_tia->scan_x;
	if(data) {
		data = (68/*Hblank*/ + 160) - data;
		data = tia_div3_table[data]; /* 3 clock counts per machine cycle */
	}
	return data;
}
/* $03   RSYNC   ---- ----  reset horizontal sync counter */
static int tia_write_RSYNC(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->last_x = p_tia->scan_x = 0; /* ※要確認 */
	return 0;
}
/* $04   NUSIZ0  00xx 0xxx  number-size player-missile 0 */
static int tia_write_NUSIZ0(int data) {
	TIA* p_tia = &tia;
	SET_REG(NUSIZ0, 0x37, 1);
	p_tia->player[0].size  = (data >> 0) & 7;
	p_tia->missile[0].size = (data >> 4) & 3;
	return 0;
}
/* $05   NUSIZ1  00xx 0xxx  number-size player-missile 1 */
static int tia_write_NUSIZ1(int data) {
	TIA* p_tia = &tia;
	SET_REG(NUSIZ1, 0x37, 1);
	p_tia->player[1].size  = (data >> 0) & 7;
	p_tia->missile[1].size = (data >> 4) & 3;
	return 0;
}
/* $06   COLUP0  xxxx xxx0  color-lum player 0 */
static int tia_write_COLUP0(int data) {
	TIA* p_tia = &tia;
	SET_REG(COLUP0, 0xfe, 1);
	data >>= 1;
#ifdef TIA_GRAYSCALE
	data = tia_grayscale_table[data];
#endif TIA_GRAYSCALE
	p_tia->clut[0] = data;
	return 0;
}
/* $07   COLUP1  xxxx xxx0  color-lum player 1 */
static int tia_write_COLUP1(int data) {
	TIA* p_tia = &tia;
	SET_REG(COLUP1, 0xfe, 1);
	data >>= 1;
#ifdef TIA_GRAYSCALE
	data = tia_grayscale_table[data];
#endif TIA_GRAYSCALE
	p_tia->clut[1] = data;
	return 0;
}
/* $08   COLUPF  xxxx xxx0  color-lum playfield */
static int tia_write_COLUPF(int data) {
	TIA* p_tia = &tia;
	SET_REG(COLUPF, 0xfe, 1);
	data >>= 1;
#ifdef TIA_GRAYSCALE
	data = tia_grayscale_table[data];
#endif TIA_GRAYSCALE
	p_tia->clut[2] = data;
	return 0;
}
/* $09   COLUBK  xxxx xxx0  color-lum background */
static int tia_write_COLUBK(int data) {
	TIA* p_tia = &tia;
	SET_REG(COLUBK, 0xfe, 1);
	data >>= 1;
#ifdef TIA_GRAYSCALE
	data = tia_grayscale_table[data];
#endif TIA_GRAYSCALE
	p_tia->clut[3] = data;
	return 0;
}
/* $0A   CTRLPF  00xx 0xxx  control playfield ball size & collisions */
static int tia_write_CTRLPF(int data) {
	TIA* p_tia = &tia;
	SET_REG(CTRLPF, 0x37, 1);
	p_tia->playfield.reflect = (data >> 0) & 1;
	p_tia->ball.size         = (data >> 4) & 3;
	return 0;
}
/* $0B   REFP0   0000 x000  reflect player 0 */
static int tia_write_REFP0(int data) {
	TIA* p_tia = &tia;
	SET_REG(REFP0, 0x08, 1);
	p_tia->player[0].reflect = (data >> 3) & 1;
	return 0;
}
/* $0C   REFP1   0000 x000  reflect player 1 */
static int tia_write_REFP1(int data) {
	TIA* p_tia = &tia;
	SET_REG(REFP1, 0x08, 1);
	p_tia->player[1].reflect = (data >> 3) & 1;
	return 0;
}
/* $0D   PF0     xxxx 0000  playfield register byte 0 */
static int tia_write_PF0(int data) {
	TIA* p_tia = &tia;
	SET_REG(PF0, 0xf0, 1);
	p_tia->playfield.graphics =
		MIRROR(p_tia->PF0) << 16 | /* ----4567 -> 4567 -------- -------- */
		       p_tia->PF1  <<  8 | /* 76543210 -> ---- 76543210 -------- */
		MIRROR(p_tia->PF2);        /* 01234567 -> ---- -------- 01234567 */
	return 0;
}
/* $0E   PF1     xxxx xxxx  playfield register byte 1 */
static int tia_write_PF1(int data) {
	TIA* p_tia = &tia;
	SET_REG(PF1, 0xff, 1);
	p_tia->playfield.graphics =
		MIRROR(p_tia->PF0) << 16 | /* ----4567 -> 4567 -------- -------- */
		       p_tia->PF1  <<  8 | /* 76543210 -> ---- 76543210 -------- */
		MIRROR(p_tia->PF2);        /* 01234567 -> ---- -------- 01234567 */
	return 0;
}
/* $0F   PF2     xxxx xxxx  playfield register byte 2 */
static int tia_write_PF2(int data) {
	TIA* p_tia = &tia;
	SET_REG(PF2, 0xff, 1);
	p_tia->playfield.graphics =
		MIRROR(p_tia->PF0) << 16 | /* ----4567 -> 4567 -------- -------- */
		       p_tia->PF1  <<  8 | /* 76543210 -> ---- 76543210 -------- */
		MIRROR(p_tia->PF2);        /* 01234567 -> ---- -------- 01234567 */
	return 0;
}
/* $10   RESP0   ---- ----  reset player 0 */
static int tia_write_RESP0(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->player[0].position = p_tia->scan_x - 68/*Hblank*/ + 5/*仕様*/;
	if(p_tia->player[0].position > 159) {
		p_tia->player[0].position = 0;
	}
	return 0;
}
/* $11   RESP1   ---- ----  reset player 1 */
static int tia_write_RESP1(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->player[1].position = p_tia->scan_x - 68/*Hblank*/ + 5/*仕様*/;
	if(p_tia->player[1].position > 159) {
		p_tia->player[1].position = 0;
	}
	return 0;
}
/* $12   RESM0   ---- ----  reset missile 0 */
static int tia_write_RESM0(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->missile[0].position = p_tia->scan_x - 68/*Hblank*/ + 4/*仕様*/;
	if(p_tia->missile[0].position > 159) {
		p_tia->missile[0].position = 0;
	}
	return 0;
}
/* $13   RESM1   ---- ----  reset missile 1 */
static int tia_write_RESM1(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->missile[1].position = p_tia->scan_x - 68/*Hblank*/ + 4/*仕様*/;
	if(p_tia->missile[1].position > 159) {
		p_tia->missile[1].position = 0;
	}
	return 0;
}
/* $14   RESBL   ---- ----  reset ball */
static int tia_write_RESBL(int data) {
	TIA* p_tia = &tia;
	tia_partial_update();
	p_tia->ball.position = p_tia->scan_x - 68/*Hblank*/ + 4/*仕様*/;
	if(p_tia->ball.position > 159) {
		p_tia->ball.position = 0;
	}
	return 0;
}
/*{{AUDIO===========================================================*/
/* 互いに関連の無い1byte書き込みなので、割り込み禁止は不要です。 */
/* $15   AUDC0   0000 xxxx  audio control 0 */
static int tia_write_AUDC0(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDC0, 0x0f, 0/*更新不要*/);
	return 0;
}
/* $16   AUDC1   0000 xxxx  audio control 1 */
static int tia_write_AUDC1(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDC1, 0x0f, 0/*更新不要*/);
	return 0;
}
/* $17   AUDF0   000x xxxx  audio frequency 0 */
static int tia_write_AUDF0(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDF0, 0x1f, 0/*更新不要*/);
	p_tia->audio[0].frequency = data + 1; /* 0..31 -> 1..32 */
	return 0;
}
/* $18   AUDF1   000x xxxx  audio frequency 1 */
static int tia_write_AUDF1(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDF1, 0x1f, 0/*更新不要*/);
	p_tia->audio[1].frequency = data + 1; /* 0..31 -> 1..32 */
	return 0;
}
/* $19   AUDV0   0000 xxxx  audio volume 0 */
static int tia_write_AUDV0(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDV0, 0x0f, 0/*更新不要*/);
	p_tia->audio[0].volume = data;
	return 0;
}
/* $1A   AUDV1   0000 xxxx  audio volume 1 */
static int tia_write_AUDV1(int data) {
	TIA* p_tia = &tia;
	SET_REG(AUDV1, 0x0f, 0/*更新不要*/);
	p_tia->audio[1].volume = data;
	return 0;
}
/*}}AUDIO===========================================================*/
/* $1B   GRP0    xxxx xxxx  graphics player 0 */
static int tia_write_GRP0(int data) {
	TIA* p_tia = &tia;
	/* * Vertical delayは、Vblankによって自動的に制御されるのではなく、
	 *     GRP0への書き込みによって、Player1      のVertical delayが制御される。
	 *     GRP1への書き込みによって、Player0とBallのVertical delayが制御される。
	 *   という、よくわからない仕様です。
	 *   [STELLA PROGRAMMER'S GUIDE (p.29) Figure 1. Vertical Delay 参照]
	 */
	if((p_tia->player[0].graphics[0] != data) ||
	   (p_tia->player[1].graphics[1] != p_tia->player[1].graphics[0])) {
		tia_partial_update();
		p_tia->player[0].graphics[0] = data;
		p_tia->player[1].graphics[1] = p_tia->player[1].graphics[0];
	}
	return 0;
}
/* $1C   GRP1    xxxx xxxx  graphics player 1 */
static int tia_write_GRP1(int data) {
	TIA* p_tia = &tia;
	/* * Vertical delayは、Vblankによって自動的に制御されるのではなく、
	 *     GRP0への書き込みによって、Player1      のVertical delayが制御される。
	 *     GRP1への書き込みによって、Player0とBallのVertical delayが制御される。
	 *   という、よくわからない仕様です。
	 *   [STELLA PROGRAMMER'S GUIDE (p.29) Figure 1. Vertical Delay 参照]
	 */
	if((p_tia->player[1].graphics[0] != data) ||
	   (p_tia->player[0].graphics[1] != p_tia->player[0].graphics[0]) ||
	   (p_tia->ball     .graphics[1] != p_tia->ball     .graphics[0])) {
		tia_partial_update();
		p_tia->player[1].graphics[0] = data;
		p_tia->player[0].graphics[1] = p_tia->player[0].graphics[0];
		p_tia->ball     .graphics[1] = p_tia->ball     .graphics[0];
	}
	return 0;
}
/* $1D   ENAM0   0000 00x0  graphics (enable) missile 0 */
static int tia_write_ENAM0(int data) {
	TIA* p_tia = &tia;
	SET_REG(ENAM0, 0x02, 1);
	p_tia->missile[0].graphics = (data >> 1) & 1;
	return 0;
}
/* $1E   ENAM1   0000 00x0  graphics (enable) missile 1 */
static int tia_write_ENAM1(int data) {
	TIA* p_tia = &tia;
	SET_REG(ENAM1, 0x02, 1);
	p_tia->missile[1].graphics = (data >> 1) & 1;
	return 0;
}
/* $1F   ENABL   0000 00x0  graphics (enable) ball */
static int tia_write_ENABL(int data) {
	TIA* p_tia = &tia;
	SET_REG(ENABL, 0x02, 1);
	p_tia->ball.graphics[0] = (data >> 1) & 1;
	return 0;
}
/* $20   HMP0    xxxx 0000  horizontal motion player 0 */
static int tia_write_HMP0(int data) {
	TIA* p_tia = &tia;
	SET_REG(HMP0, 0xf0, 0/*HMOVEが呼ばれるまで更新不要*/);
	return 0;
}
/* $21   HMP1    xxxx 0000  horizontal motion player 1 */
static int tia_write_HMP1(int data) {
	TIA* p_tia = &tia;
	SET_REG(HMP1, 0xf0, 0/*HMOVEが呼ばれるまで更新不要*/);
	return 0;
}
/* $22   HMM0    xxxx 0000  horizontal motion missile 0 */
static int tia_write_HMM0(int data) {
	TIA* p_tia = &tia;
	SET_REG(HMM0, 0xf0, 0/*HMOVEが呼ばれるまで更新不要*/);
	return 0;
}
/* $23   HMM1    xxxx 0000  horizontal motion missile 1 */
static int tia_write_HMM1(int data) {
	TIA* p_tia = &tia;
	SET_REG(HMM1, 0xf0, 0/*HMOVEが呼ばれるまで更新不要*/);
	return 0;
}
/* $24   HMBL    xxxx 0000  horizontal motion ball */
static int tia_write_HMBL(int data) {
	TIA* p_tia = &tia;
	SET_REG(HMBL, 0xf0, 0/*HMOVEが呼ばれるまで更新不要*/);
	return 0;
}
/* $25   VDELP0  0000 000x  vertical delay player 0 */
static int tia_write_VDELP0(int data) {
	TIA* p_tia = &tia;
	SET_REG(player[0].delay, 0x01, 1);
	return 0;
}
/* $26   VDELP1  0000 000x  vertical delay player 1 */
static int tia_write_VDELP1(int data) {
	TIA* p_tia = &tia;
	SET_REG(player[1].delay, 0x01, 1);
	return 0;
}
/* $27   VDELBL  0000 000x  vertical delay ball */
static int tia_write_VDELBL(int data) {
	TIA* p_tia = &tia;
	SET_REG(ball.delay     , 0x01, 1);
	return 0;
}
/* $28   RESMP0  0000 00x0  reset missile 0 to player 0 */
static int tia_write_RESMP0(int data) {
	TIA* p_tia = &tia;
	SET_REG(RESMP0, 0x02, 1);
	p_tia->missile[0].reset = (data >> 1) & 1;
	if(!p_tia->missile[0].reset) { /* missile-player reset 解除時に位置合わせ */
		p_tia->missile[0].position = p_tia->player[0].position;
	}
	return 0;
}
/* $29   RESMP1  0000 00x0  reset missile 1 to player 1 */
static int tia_write_RESMP1(int data) {
	TIA* p_tia = &tia;
	SET_REG(RESMP1, 0x02, 1);
	p_tia->missile[1].reset = (data >> 1) & 1;
	if(!p_tia->missile[1].reset) { /* missile-player reset 解除時に位置合わせ */
		p_tia->missile[1].position = p_tia->player[1].position;
	}
	return 0;
}
/* $2A   HMOVE   ---- ----  apply horizontal motion */
static int tia_write_HMOVE(int data) {
	TIA* p_tia = &tia;
	if(p_tia->HMP0 ||
	   p_tia->HMP1 ||
	   p_tia->HMM0 ||
	   p_tia->HMM1 ||
	   p_tia->HMBL) {
		tia_partial_update();
		H_MOTION(HMP0, player[0]);
		H_MOTION(HMP1, player[1]);
		H_MOTION(HMM0, missile[0]);
		H_MOTION(HMM1, missile[1]);
		H_MOTION(HMBL, ball);
	}
	return 0;
}
/* $2B   HMCLR   ---- ----  clear horizontal motion registers */
static int tia_write_HMCLR(int data) {
	TIA* p_tia = &tia;
	/* 更新不要 */
	p_tia->HMP0 = 0;
	p_tia->HMP1 = 0;
	p_tia->HMM0 = 0;
	p_tia->HMM1 = 0;
	p_tia->HMBL = 0;
	return 0;
}
/* $2C   CXCLR   ---- ----  clear collision latches */
int tia_write_CXCLR(int data) {
	TIA* p_tia = &tia;
	/* Collisionクリア前に、現時刻までの画面更新とCollision検出が必要です!!
	 * もしここで更新しないと、
	 *	時刻t0にCollision検出、時刻t1にクリア、最終状態はCollision無し
	 * という場合に、誤って、
	 *	時刻t1のクリアを先に処理、時刻t0のCollision検出を遅れて処理、最終状態はCollision有り
	 * というふうに、逆の結果になってしまう可能性があります。
	 */
	tia_partial_update(); /* 必要 */
	p_tia->collision = 0;
	return 0;
}
#undef SET_REG
#undef H_MOTION

int (*tia_write_table[0x2c+1])(int data) = { /* tia_write()から利用します */
	tia_write_VSYNC,	/* $00 */
	tia_write_VBLANK,	/* $01 */
	tia_write_WSYNC,	/* $02 */
	tia_write_RSYNC,	/* $03 */
	tia_write_NUSIZ0,	/* $04 */
	tia_write_NUSIZ1,	/* $05 */
	tia_write_COLUP0,	/* $06 */
	tia_write_COLUP1,	/* $07 */
	tia_write_COLUPF,	/* $08 */
	tia_write_COLUBK,	/* $09 */
	tia_write_CTRLPF,	/* $0A */
	tia_write_REFP0,	/* $0B */
	tia_write_REFP1,	/* $0C */
	tia_write_PF0,		/* $0D */
	tia_write_PF1,		/* $0E */
	tia_write_PF2,		/* $0F */
	tia_write_RESP0,	/* $10 */
	tia_write_RESP1,	/* $11 */
	tia_write_RESM0,	/* $12 */
	tia_write_RESM1,	/* $13 */
	tia_write_RESBL,	/* $14 */
	tia_write_AUDC0,	/* $15 */
	tia_write_AUDC1,	/* $16 */
	tia_write_AUDF0,	/* $17 */
	tia_write_AUDF1,	/* $18 */
	tia_write_AUDV0,	/* $19 */
	tia_write_AUDV1,	/* $1A */
	tia_write_GRP0,		/* $1B */
	tia_write_GRP1,		/* $1C */
	tia_write_ENAM0,	/* $1D */
	tia_write_ENAM1,	/* $1E */
	tia_write_ENABL,	/* $1F */
	tia_write_HMP0,		/* $20 */
	tia_write_HMP1,		/* $21 */
	tia_write_HMM0,		/* $22 */
	tia_write_HMM1,		/* $23 */
	tia_write_HMBL,		/* $24 */
	tia_write_VDELP0,	/* $25 */
	tia_write_VDELP1,	/* $26 */
	tia_write_VDELBL,	/* $27 */
	tia_write_RESMP0,	/* $28 */
	tia_write_RESMP1,	/* $29 */
	tia_write_HMOVE,	/* $2A */
	tia_write_HMCLR,	/* $2B */
	tia_write_CXCLR,	/* $2C */
};

static int tia_read_CXM0P() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 0)) & 3) << 6;
}
static int tia_read_CXM1P() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 1)) & 3) << 6;
}
static int tia_read_CXP0FB() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 2)) & 3) << 6;
}
static int tia_read_CXP1FB() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 3)) & 3) << 6;
}
static int tia_read_CXM0FB() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 4)) & 3) << 6;
}
static int tia_read_CXM1FB() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 5)) & 3) << 6;
}
static int tia_read_CXBLPF() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 6)) & 3) << 6;
}
static int tia_read_CXPPMM() {
	TIA* p_tia = &tia;
	tia_partial_update(); /* Collision読み出し前に、現時刻までの画面更新とCollision検出が必要です!! */
	return ((p_tia->collision >> (2 * 7)) & 3) << 6;
}
static int tia_read_INPT0() {
	TIA* p_tia = &tia;
	return p_tia->INPT0;
}
static int tia_read_INPT1() {
	TIA* p_tia = &tia;
	return p_tia->INPT1;
}
static int tia_read_INPT2() {
	TIA* p_tia = &tia;
	return p_tia->INPT2;
}
static int tia_read_INPT3() {
	TIA* p_tia = &tia;
	return p_tia->INPT3;
}
static int tia_read_INPT4() {
	TIA* p_tia = &tia;
	return p_tia->INPT4;
}
static int tia_read_INPT5() {
	TIA* p_tia = &tia;
	return p_tia->INPT5;
}
static int tia_read_ERROR() {
	return 0; /* 不正読み出し */
}

int (*tia_read_table[0xf+1])() = { /* tia_read()から利用します */
	tia_read_CXM0P,		/* $0 */
	tia_read_CXM1P,		/* $1 */
	tia_read_CXP0FB,	/* $2 */
	tia_read_CXP1FB,	/* $3 */
	tia_read_CXM0FB,	/* $4 */
	tia_read_CXM1FB,	/* $5 */
	tia_read_CXBLPF,	/* $6 */
	tia_read_CXPPMM,	/* $7 */
	tia_read_INPT0,		/* $8 */
	tia_read_INPT1,		/* $9 */
	tia_read_INPT2,		/* $A */
	tia_read_INPT3,		/* $B */
	tia_read_INPT4,		/* $C */
	tia_read_INPT5,		/* $D */
	tia_read_ERROR,		/* $E:不正読み出し */
	tia_read_ERROR,		/* $F:不正読み出し */
};

/* tia_read()はtia_write()に較べて使用頻度が低く、全体の高速化にほとんど影響がありません。
 * 高速RAMの容量節約のために、tia_read()は高速RAMに配置せず、SRAMに配置することにします。
 */
#ifndef TIA_ASM
int
tia_read(int now, int addr)
{
	tia_scan_progress(now);

	return tia_read_table[addr & 0xf]();
}
#else /*TIA_ASM*/
asm("
	.code
	.align	1
	.global	tia_read
tia_read:
	xcall.d	tia_scan_progress	; tia_scan_progress(now) (tia_scan_progress()のアセンブラ実装は、%r13-15を破壊しません)
	and	%r13, 0xf		; *delay*
	;
	xsll	%r13, 2
	ext	tia_read_table@ah	; return tia_read_table[addr]()
	ext	tia_read_table@al
	ld.w	%r13, [%r13]
	jp	%r13			; !INTERLOCK!
");
#endif /*TIA_ASM*/

void
tia_set_i4_i5(int i4, int i5)
{
	TIA* p_tia = &tia;

	p_tia->i4 = i4;
	p_tia->i5 = i5;

	if(p_tia->VBLANK & (1<<6)) {
		/* enable I4 I5 latches  ... Hi→Loのみ */
		p_tia->INPT4 &= i4 << 7;
		p_tia->INPT5 &= i5 << 7;
	} else {
		/* disable I4 I5 latches ... Hi⇔Lo直接 */
		p_tia->INPT4  = i4 << 7;
		p_tia->INPT5  = i5 << 7;
	}
}

/*{{AUDIO===========================================================*/

void (*tia_audio_mix_table[])(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/]) = {
	tia_audio_mix0,
	tia_audio_mix1,
	tia_audio_mix2,
	tia_audio_mix3,
	tia_audio_mix4,
	tia_audio_mix4, /*tia_audio_mix5*/
	tia_audio_mix6,
	tia_audio_mix7,
	tia_audio_mix8,
	tia_audio_mix9,
	tia_audio_mixA,
	tia_audio_mix0, /*tia_audio_mixB*/
	tia_audio_mixC,
	tia_audio_mixC, /*tia_audio_mixD*/
	tia_audio_mixE,
	tia_audio_mixF,
};

void
tia_mix(short wbuff[/*TIABUFLEN*/])
{
	TIA* p_tia = &tia;

	memset(wbuff, 0, sizeof(short) * TIABUFLEN);

	tia_audio_mix_table[p_tia->AUDC0](&p_tia->audio[0], wbuff);
	tia_audio_mix_table[p_tia->AUDC1](&p_tia->audio[1], wbuff);

	tia_volume_shift(wbuff);
}

#ifndef TIA_ASM

/* 4bit: tap=0,1 */
#define UPDATE_LFSR4 do {			\
	int x = (lfsr4 ^ (lfsr4 >> 1)) & 1;	\
	lfsr4 = (lfsr4 >> 1) | (x << (4 - 1));	\
} while(0)
/* 5bit: tap=0,3 */
#define UPDATE_LFSR5 do {			\
	int x = (lfsr5 ^ (lfsr5 >> 3)) & 1;	\
	lfsr5 = (lfsr5 >> 1) | (x << (5 - 1));	\
} while(0)
/* 9bit: tap=0,5 */
#define UPDATE_LFSR9 do {			\
	int x = (lfsr9 ^ (lfsr9 >> 5)) & 1;	\
	lfsr9 = (lfsr9 >> 1) | (x << (9 - 1));	\
} while(0)

/* clock/Freq pulse ----------------> Polar toggle -> */
static void
tia_audio_mix4C(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/], int clock)
{
	int i;
	int volume;
	int period;
	int count;
	int polar;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	polar = audio->polar;	/* 取り出し */

	output = polar ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= clock;
		if(count < 0) { /*Carry*/
			do {
				polar ^= 1;
			} while((count += period) < 0); /*NonCarry*/
			output = polar ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->polar = polar;	/* 書き戻し */
}

/* clock/Freq pulse -> Div31 pulse -> Polar toggle -> */
static void
tia_audio_mix6E(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/], int clock)
{
	int i;
	int volume;
	int period;
	int count;
	int div31;
	int polar;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	div31 = audio->div31;	/* 取り出し */
	polar = audio->polar;	/* 取り出し */

	output = polar ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= clock;
		if(count < 0) { /*Carry*/
			do {
				if(--div31 < 0) {
					div31 += 31;
				}
				if(div31 == 0 || div31 == 13) { /*13:18(仕様)*/
					polar ^= 1;
				}
			} while((count += period) < 0); /*NonCarry*/
			output = polar ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->div31 = div31;	/* 書き戻し */
	audio->polar = polar;	/* 書き戻し */
}

/* clock/Freq pulse -> Poly5 pulse -> Polar toggle -> */
static void
tia_audio_mix7F(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/], int clock)
{
	int i;
	int volume;
	int period;
	int count;
	int lfsr5;
	int polar;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	lfsr5 = audio->lfsr5;	/* 取り出し */
	polar = audio->polar;	/* 取り出し */

	output = polar ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= clock;
		if(count < 0) { /*Carry*/
			do {
				UPDATE_LFSR5;
				if(lfsr5 & 1) {
					polar ^= 1;
				}
			} while((count += period) < 0); /*NonCarry*/
			output = polar ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->lfsr5 = lfsr5;	/* 書き戻し */
	audio->polar = polar;	/* 書き戻し */
}

/*                                               1 -> */
void
tia_audio_mix0(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;

	volume = audio->volume;
	if(!volume) {
		return;
	}

	i = TIABUFLEN;
	do {
		*wbuff++ += volume;
	} while(--i);
}

/* 30KHz/Freq pulse ----------------> Poly4 output -> */
void
tia_audio_mix1(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int lfsr4;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	lfsr4 = audio->lfsr4;	/* 取り出し */

	output = (lfsr4 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				UPDATE_LFSR4;
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr4 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->lfsr4 = lfsr4;	/* 書き戻し */
}

/* 30KHz/Freq pulse -> Div31 pulse -> Poly4 output -> */
void
tia_audio_mix2(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int div31;
	int lfsr4;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	div31 = audio->div31;	/* 取り出し */
	lfsr4 = audio->lfsr4;	/* 取り出し */

	output = (lfsr4 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				if(--div31 < 0) {
					div31 += 31;
				}
				if(div31 == 0 || div31 == 13) { /*13:18(仕様)*/
					UPDATE_LFSR4;
				}
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr4 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->div31 = div31;	/* 書き戻し */
	audio->lfsr4 = lfsr4;	/* 書き戻し */
}

/* 30KHz/Freq pulse -> Poly5 pulse -> Poly4 output -> */
void
tia_audio_mix3(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int lfsr5;
	int lfsr4;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	lfsr5 = audio->lfsr5;	/* 取り出し */
	lfsr4 = audio->lfsr4;	/* 取り出し */

	output = (lfsr4 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				UPDATE_LFSR5;
				if(lfsr5 & 1) {
					UPDATE_LFSR4;
				}
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr4 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->lfsr5 = lfsr5;	/* 書き戻し */
	audio->lfsr4 = lfsr4;	/* 書き戻し */
}

/* 30KHz/Freq pulse ----------------> Polar toggle -> */
void
tia_audio_mix4(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix4C(audio, wbuff, TIA_CLOCK / 114);
}

/* 30KHz/Freq pulse -> Div31 pulse -> Polar toggle -> */
void
tia_audio_mix6(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix6E(audio, wbuff, TIA_CLOCK / 114);
}

/* 30KHz/Freq pulse -> Poly5 pulse -> Polar toggle -> */
void
tia_audio_mix7(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix7F(audio, wbuff, TIA_CLOCK / 114);
}

/* 30KHz/Freq pulse ----------------> Poly9 output -> */
void
tia_audio_mix8(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int lfsr9;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	lfsr9 = audio->lfsr9;	/* 取り出し */

	output = (lfsr9 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				UPDATE_LFSR9;
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr9 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->lfsr9 = lfsr9;	/* 書き戻し */
}

/* 30KHz/Freq pulse ----------------> Poly5 output -> */
void
tia_audio_mix9(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int lfsr5;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	lfsr5 = audio->lfsr5;	/* 取り出し */

	output = (lfsr5 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				UPDATE_LFSR5;
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr5 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->lfsr5 = lfsr5;	/* 書き戻し */
}

/* 30KHz/Freq pulse -> Div31 pulse -> Poly5 output -> */
void
tia_audio_mixA(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	int i;
	int volume;
	int period;
	int count;
	int div31;
	int lfsr5;
	int output;

	volume = audio->volume;
	if(!volume) {
		return;
	}
	period = SPEAKER_FREQUENCY * audio->frequency;

	count = audio->count;	/* 取り出し */
	div31 = audio->div31;	/* 取り出し */
	lfsr5 = audio->lfsr5;	/* 取り出し */

	output = (lfsr5 & 1) ? volume : -volume;

	i = TIABUFLEN;
	do {
		count -= (TIA_CLOCK / 114);
		if(count < 0) { /*Carry*/
			do {
				if(--div31 < 0) {
					div31 += 31;
				}
				if(div31 == 0 || div31 == 13) { /*13:18(仕様)*/
					UPDATE_LFSR5;
				}
			} while((count += period) < 0); /*NonCarry*/
			output = (lfsr5 & 1) ? volume : -volume;
		}
		*wbuff++ += output;
	} while(--i);

	audio->count = count;	/* 書き戻し */
	audio->div31 = div31;	/* 書き戻し */
	audio->lfsr5 = lfsr5;	/* 書き戻し */
}

/* 10KHz/Freq pulse ----------------> Polar toggle -> */
void
tia_audio_mixC(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix4C(audio, wbuff, VCS_CLOCK / 114);
}

/* 10KHz/Freq pulse -> Div31 pulse -> Polar toggle -> */
void
tia_audio_mixE(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix6E(audio, wbuff, VCS_CLOCK / 114);
}

/* 10KHz/Freq pulse -> Poly5 pulse -> Polar toggle -> */
void
tia_audio_mixF(TIAAUDIO* audio, short wbuff[/*TIABUFLEN*/])
{
	tia_audio_mix7F(audio, wbuff, VCS_CLOCK / 114);
}

#undef UPDATE_LFSR4
#undef UPDATE_LFSR5
#undef UPDATE_LFSR9

#endif /*TIA_ASM*/

/*}}AUDIO===========================================================*/

/****************************************************************************
 *	PIA (Peripheral Interface Adaptor) (MOS6532, RIOTとも呼ばれます)
 ****************************************************************************/

PIA pia;

void
pia_reset(int now)
{
	PIA* p_pia = &pia;

	memset(p_pia, 0, sizeof(PIA));

	p_pia->now = now;
}

static void
pia_timer_progress(int now)
{
	PIA* p_pia = &pia;
	//
	unsigned clock;

	clock = now - p_pia->now;
	if(clock > PIA_CLOCK / 60) { /* Safety */
		clock = PIA_CLOCK / 60;
	}
	p_pia->now = now;

	if(p_pia->timer < clock) {
		p_pia->shift = 0; /* アンダーフロー後は1clock毎に-1 */
	}
	p_pia->timer -= clock;
}

void
pia_write(int now, int addr, int data)
{
	PIA* p_pia = &pia;

	pia_timer_progress(now);

	switch(addr) {
	case 0x280: /* SWCHA   Port A; input or output (read or write) */
		p_pia->SWCHA = data;
		return;
	case 0x281: /* SWACNT  Port A DDR; 0 = input, 1 = output */
		p_pia->SWACNT = data;
		return;
	case 0x282: /* SWCHB   Port B; console switches (read only) */
		p_pia->SWCHB = data;
		return;
	case 0x283: /* SWBCNT  Port B DDR; hardwired as input */
		p_pia->SWBCNT = data;
		return;
	//case 0x284: /* INTIM   Timer output (read only) */
	case 0x294: /* TIM1T   set    1 clock interval (838   nsec/interval) */
		p_pia->shift = 0;
		p_pia->timer = data << p_pia->shift;
		return;
	case 0x295: /* TIM8T   set    8 clock interval (  6.7 usec/interval) */
		p_pia->shift = 3;
		p_pia->timer = data << p_pia->shift;
		return;
	case 0x296: /* TIM64T  set   64 clock interval ( 53.6 usec/interval) */
		p_pia->shift = 6;
		p_pia->timer = data << p_pia->shift;
		return;
	case 0x297: /* T1024T  set 1024 clock interval (858.2 usec/interval) */
		p_pia->shift = 10;
		p_pia->timer = data << p_pia->shift;
		return;
	}
}

int
pia_read(int now, int addr)
{
	PIA* p_pia = &pia;

	pia_timer_progress(now);

	switch(addr) {
	case 0x280: /* SWCHA   Port A; input or output (read or write) */
		return p_pia->SWCHA;
	case 0x281: /* SWACNT  Port A DDR; 0 = input, 1 = output */
		return p_pia->SWACNT;
	case 0x282: /* SWCHB   Port B; console switches (read only) */
		return p_pia->SWCHB;
	case 0x283: /* SWBCNT  Port B DDR; hardwired as input */
		return p_pia->SWBCNT;
	case 0x284: /* INTIM   Timer output (read only) */
		return (unsigned char)(p_pia->timer >> p_pia->shift);
	//case 0x294: /* TIM1T   set    1 clock interval (838   nsec/interval) */
	//case 0x295: /* TIM8T   set    8 clock interval (  6.7 usec/interval) */
	//case 0x296: /* TIM64T  set   64 clock interval ( 53.6 usec/interval) */
	//case 0x297: /* T1024T  set 1024 clock interval (858.2 usec/interval) */
	}

	return 0;
}
