1 /* Implementation of SPIM compatible console device.
2    Copyright 2002, 2003 Paul Twohey.
3 
4 This file is part of VMIPS.
5 
6 VMIPS is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10 
11 VMIPS is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with VMIPS; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19 
20 #include "clock.h"
21 #include "mapper.h"
22 #include "spimconsole.h"
23 #include "spimconsreg.h"
24 #include "vmips.h"
25 #include <cassert>
26 
SpimConsoleDevice(Clock * clock)27 SpimConsoleDevice::SpimConsoleDevice (Clock *clock)
28 	: TerminalController (clock, KEYBOARD_POLL_NS, KEYBOARD_REPOLL_NS,
29                           DISPLAY_READY_DELAY_NS),
30 	  DeviceMap (36),
31 	  trigger (0), clock_interrupt (false), clock_state (UNREADY)
32 {
33 	display_interrupt_enable[0] = display_interrupt_enable[1] = false;
34 	keyboard_interrupt_enable[0] = keyboard_interrupt_enable[1] = false;
35 
36 	trigger = new ClockTrigger( this );
37 	clock->add_deferred_task( trigger, CLOCK_TRIGGER_NS );
38 }
39 
~SpimConsoleDevice()40 SpimConsoleDevice::~SpimConsoleDevice()
41 {
42 	assert( trigger );
43 
44 	trigger->cancel();
45 }
46 
unready_display(int line,char data)47 void SpimConsoleDevice::unready_display( int line, char data )
48 {
49 	TerminalController::unready_display( line, data );
50 	deassertInt( line == 0 ? IRQ4 : IRQ6 );
51 }
52 
ready_display(int line)53 void SpimConsoleDevice::ready_display( int line )
54 {
55 	TerminalController::ready_display( line );
56 	if( display_interrupt_enable[line] )
57 		assertInt( line == 0 ? IRQ4 : IRQ6 );
58 }
59 
unready_keyboard(int line)60 void SpimConsoleDevice::unready_keyboard( int line )
61 {
62 	TerminalController::unready_keyboard( line );
63 	deassertInt( line == 0 ? IRQ3 : IRQ5 );
64 }
65 
ready_keyboard(int line)66 void SpimConsoleDevice::ready_keyboard( int line )
67 {
68 	TerminalController::ready_keyboard( line );
69 	if( keyboard_interrupt_enable[line] )
70 		assertInt( line == 0 ? IRQ3 : IRQ5 );
71 }
72 
unready_clock()73 void SpimConsoleDevice::unready_clock()
74 {
75 	clock_state = UNREADY;
76 	deassertInt( IRQ2 );
77 }
78 
ready_clock()79 void SpimConsoleDevice::ready_clock()
80 {
81 	clock_state = READY;
82 
83 	trigger = new ClockTrigger( this );
84 	clock->add_deferred_task( trigger, CLOCK_TRIGGER_NS );
85 
86 	if( clock_interrupt )
87 		assertInt( IRQ2 );
88 }
89 
fetch_word(uint32 offset,int mode,DeviceExc * client)90 uint32 SpimConsoleDevice::fetch_word( uint32 offset, int mode,
91 				      DeviceExc *client)
92 {
93 	uint32 word = 0;
94 
95 	switch( offset / 4 ) {
96 	case 0:		// keyboard 1 control
97 		word = keyboard_interrupt_enable[0] ? CTL_IE : 0;
98 		if( line_connected(0) )
99 			word |= lines[0].keyboard_state;
100 		if (!keyboard_interrupt_enable[0])
101 			deassertInt (IRQ3);
102 		break;
103 	case 1:		// keyboard 1 data
104 		if( line_connected(0) ) {
105 			unready_keyboard( 0 );
106 			word = lines[0].keyboard_char;
107 		}
108 		break;
109 	case 2:		// display 1 control
110 		word = display_interrupt_enable[0] ? CTL_IE : 0;
111 		if( line_connected(0) )
112 			word |= lines[0].display_state;
113 		else
114 			word |= CTL_RDY;
115 		if (!display_interrupt_enable[0])
116 			deassertInt (IRQ4);
117 		break;
118 	case 3:		// display 1 data
119 		break;
120 	case 4:		// keyboard 2 control
121 		word = keyboard_interrupt_enable[1] ? CTL_IE : 0;
122 		if( line_connected(1) )
123 			word |= lines[1].keyboard_state;
124 		if (!keyboard_interrupt_enable[1])
125 			deassertInt (IRQ5);
126 		break;
127 	case 5:		// keyboard 2 data
128 		if( line_connected(1) ) {
129 			unready_keyboard( 1 );
130 			word = lines[1].keyboard_char;
131 		}
132 		break;
133 	case 6:		// display 2 control
134 		word = display_interrupt_enable[1] ? CTL_IE : 0;
135 		if( line_connected(1) )
136 			word |= lines[1].display_state;
137 		else
138 			word |= CTL_RDY;
139 		if (!display_interrupt_enable[1])
140 			deassertInt (IRQ6);
141 		break;
142 	case 7:		// display 2 data
143 		break;
144 	case 8:		// clock control
145 		word = clock_interrupt ? CTL_IE : 0;
146 		word |= clock_state;
147 		unready_clock();
148 		break;
149 	default:
150 		assert( ! "reached" );
151 	}
152 	return machine->physmem->mips_to_host_word(word);
153 }
154 
store_word(uint32 offset,uint32 odata,DeviceExc * client)155 void SpimConsoleDevice::store_word( uint32 offset, uint32 odata,
156 				    DeviceExc *client )
157 {
158 	uint32 data = machine->physmem->host_to_mips_word(odata);
159 
160 	switch( offset / 4 ) {
161 	case 0:		// keyboard 1 control
162 		keyboard_interrupt_enable[0] = data & CTL_IE;
163 		if( line_connected(0) && keyboard_interrupt_enable[0]
164 		    && lines[0].display_state == READY )
165 			assertInt( IRQ3 );
166 		break;
167 	case 1:		// keyboard 1 data
168 		break;
169 	case 2:		// display 1 control
170 		display_interrupt_enable[0] = data & CTL_IE;
171 		if( line_connected(0) && display_interrupt_enable[0]
172 		    && lines[0].display_state == READY )
173 			assertInt( IRQ4 );
174 		break;
175 	case 3:		// display 1 data
176 		if( line_connected(0) )
177 			unready_display( 0, data );
178 		break;
179 	case 4:		// keyboard 2 control
180 		keyboard_interrupt_enable[1] = data & CTL_IE;
181 		if( line_connected(1) && keyboard_interrupt_enable[1] &&
182 		    lines[1].keyboard_state == READY )
183 			assertInt( IRQ5 );
184 		break;
185 	case 5:		// keyboard 2 data
186 		break;
187 	case 6:		// display 2 control
188 		display_interrupt_enable[1] = data & CTL_IE;
189 		if( line_connected(1) && display_interrupt_enable[1] &&
190 		    lines[1].display_state == READY )
191 			assertInt( IRQ6 );
192 		break;
193 	case 7:		// display 2 data
194 		if( line_connected(1) )
195 			unready_display( 1, data );
196 		break;
197 	case 8:		// clock control
198 		clock_interrupt = data & CTL_IE;
199 		if( clock_interrupt && clock_state == READY )
200 			assertInt( IRQ2 );
201 		break;
202 	default:
203 		assert( ! "reached" );
204 	}
205 }
206 
descriptor_str() const207 const char *SpimConsoleDevice::descriptor_str() const
208 {
209 	return "SPIM console";
210 }
211 
212 
ClockTrigger(SpimConsoleDevice * console)213 SpimConsoleDevice::ClockTrigger::ClockTrigger( SpimConsoleDevice *console )
214 	: console( console )
215 {
216 	assert( console );
217 }
218 
~ClockTrigger()219 SpimConsoleDevice::ClockTrigger::~ClockTrigger()
220 {
221 }
222 
real_task()223 void SpimConsoleDevice::ClockTrigger::real_task()
224 {
225 	console->ready_clock();
226 }
227