openMSX
VLM5030.cc
Go to the documentation of this file.
1 /*
2  vlm5030.c
3 
4  VLM5030 emulator
5 
6  Written by Tatsuyuki Satoh
7  Based on TMS5220 simulator (tms5220.c)
8 
9  note:
10  memory read cycle(==sampling rate) = 122.9u(440clock)
11  interpolator (LC8109 = 2.5ms) = 20 * samples(125us)
12  frame time (20ms) = 4 * interpolator
13  9bit DAC is composed of 5bit Physical and 3bitPWM.
14 
15  todo:
16  Noise Generator circuit without 'rand()' function.
17 
18 ----------- command format (Analytical result) ----------
19 
20 1)end of speech (8bit)
21 :00000011:
22 
23 2)silent some frame (8bit)
24 :????SS01:
25 
26 SS : number of silent frames
27  00 = 2 frame
28  01 = 4 frame
29  10 = 6 frame
30  11 = 8 frame
31 
32 3)-speech frame (48bit)
33 function: 6th : 5th : 4th : 3rd : 2nd : 1st :
34 end : --- : --- : --- : --- : --- :00000011:
35 silent : --- : --- : --- : --- : --- :0000SS01:
36 speech :11111122:22233334:44455566:67778889:99AAAEEE:EEPPPPP0:
37 
38 EEEEE : energy : volume 0=off,0x1f=max
39 PPPPP : pitch : 0=noize , 1=fast,0x1f=slow
40 111111 : K1 : 48=off
41 22222 : K2 : 0=off,1=+min,0x0f=+max,0x10=off,0x11=+max,0x1f=-min
42  : 16 == special function??
43 3333 : K3 : 0=off,1=+min,0x07=+max,0x08=-max,0x0f=-min
44 4444 : K4 :
45 555 : K5 : 0=off,1=+min,0x03=+max,0x04=-max,0x07=-min
46 666 : K6 :
47 777 : K7 :
48 888 : K8 :
49 999 : K9 :
50 AAA : K10 :
51 
52  ---------- chirp table information ----------
53 
54 DAC PWM cycle == 88system clock , (11clock x 8 pattern) = 40.6KHz
55 one chirp == 5 x PWM cycle == 440systemclock(8,136Hz)
56 
57 chirp 0 : volume 10- 8 : with filter
58 chirp 1 : volume 8- 6 : with filter
59 chirp 2 : volume 6- 4 : with filter
60 chirp 3 : volume 4 : no filter ??
61 chirp 4- 5: volume 4- 2 : with filter
62 chirp 6-11: volume 2- 0 : with filter
63 chirp 12-..: vokume 0 : silent
64 
65  ---------- digial output information ----------
66  when ME pin = high , some status output to A0..15 pins
67 
68  A0..8 : DAC output value (abs)
69  A9 : DAC sign flag , L=minus,H=Plus
70  A10 : energy reload flag (pitch pulse)
71  A11..15 : unknown
72 
73  [DAC output value(signed 6bit)] = A9 ? A0..8 : -(A0..8)
74 
75 */
76 
77 #include "VLM5030.hh"
78 #include "ResampledSoundDevice.hh"
79 #include "Rom.hh"
80 #include "DeviceConfig.hh"
81 #include "XMLElement.hh"
82 #include "FileOperations.hh"
83 #include "serialize.hh"
84 #include "memory.hh"
85 #include "random.hh"
86 #include <cstring>
87 #include <cstdint>
88 
89 namespace openmsx {
90 
91 class VLM5030::Impl final : public ResampledSoundDevice
92 {
93 public:
94  Impl(const std::string& name, const std::string& desc,
95  const std::string& romFilename, const DeviceConfig& config);
96  ~Impl();
97 
98  void reset();
99  void writeData(byte data);
100  void writeControl(byte data, EmuTime::param time);
101  bool getBSY(EmuTime::param time) const;
102 
103  template<typename Archive>
104  void serialize(Archive& ar, unsigned version);
105 
106 private:
107  void setRST(bool pin);
108  void setVCU(bool pin);
109  void setST (bool pin);
110 
111  // SoundDevice
112  void generateChannels(int** bufs, unsigned num) override;
113 
114  void setupParameter(byte param);
115  int getBits(unsigned sbit, unsigned bits);
116  int parseFrame();
117 
118  std::unique_ptr<Rom> rom;
119  int address_mask;
120 
121  // state of option paramter
122  int frame_size;
123  int pitch_offset;
124 
125  // these contain data describing the current and previous voice frames
126  // these are all used to contain the current state of the sound generation
127  unsigned current_energy;
128  unsigned current_pitch;
129  int current_k[10];
130  int x[10];
131 
132  word address;
133  word vcu_addr_h;
134 
135  int16_t old_k[10];
136  int16_t new_k[10];
137  int16_t target_k[10];
138  word old_energy;
139  word new_energy;
140  word target_energy;
141  byte old_pitch;
142  byte new_pitch;
143  byte target_pitch;
144 
145  byte interp_step;
146  byte interp_count; // number of interp periods
147  byte sample_count; // sample number within interp
148  byte pitch_count;
149 
150  byte latch_data;
151  byte parameter;
152  byte phase;
153  bool pin_BSY;
154  bool pin_ST;
155  bool pin_VCU;
156  bool pin_RST;
157 };
158 
159 // interpolator per frame
160 static const int FR_SIZE = 4;
161 // samples per interpolator
162 static const int IP_SIZE_SLOWER = 240 / FR_SIZE;
163 static const int IP_SIZE_SLOW = 200 / FR_SIZE;
164 static const int IP_SIZE_NORMAL = 160 / FR_SIZE;
165 static const int IP_SIZE_FAST = 120 / FR_SIZE;
166 static const int IP_SIZE_FASTER = 80 / FR_SIZE;
167 
168 // phase value
169 enum {
177 };
178 
179 // speed parameter
180 // SPC SPB SPA
181 // 1 0 1 more slow (05h) : 42ms (150%) : 60sample
182 // 1 1 x slow (06h,07h) : 34ms (125%) : 50sample
183 // x 0 0 normal (00h,04h) : 25.6ms (100%) : 40samplme
184 // 0 0 1 fast (01h) : 20.2ms (75%) : 30sample
185 // 0 1 x more fast (02h,03h) : 12.2ms (50%) : 20sample
186 static const int VLM5030_speed_table[8] =
187 {
188  IP_SIZE_NORMAL,
189  IP_SIZE_FAST,
190  IP_SIZE_FASTER,
191  IP_SIZE_FASTER,
192  IP_SIZE_NORMAL,
193  IP_SIZE_SLOWER,
194  IP_SIZE_SLOW,
195  IP_SIZE_SLOW
196 };
197 
198 // ROM Tables
199 
200 // This is the energy lookup table
201 
202 // sampled from real chip
203 static word energytable[0x20] =
204 {
205  0, 2, 4, 6, 10, 12, 14, 18, // 0-7
206  22, 26, 30, 34, 38, 44, 48, 54, // 8-15
207  62, 68, 76, 84, 94,102,114,124, // 16-23
208  136,150,164,178,196,214,232,254 // 24-31
209 };
210 
211 // This is the pitch lookup table
212 static const byte pitchtable [0x20] =
213 {
214  1, // 0 : random mode
215  22, // 1 : start=22
216  23, 24, 25, 26, 27, 28, 29, 30, // 2- 9 : 1step
217  32, 34, 36, 38, 40, 42, 44, 46, // 10-17 : 2step
218  50, 54, 58, 62, 66, 70, 74, 78, // 18-25 : 4step
219  86, 94, 102,110,118,126 // 26-31 : 8step
220 };
221 
222 static const int16_t K1_table[] = {
223  -24898, -25672, -26446, -27091, -27736, -28252, -28768, -29155,
224  -29542, -29929, -30316, -30574, -30832, -30961, -31219, -31348,
225  -31606, -31735, -31864, -31864, -31993, -32122, -32122, -32251,
226  -32251, -32380, -32380, -32380, -32509, -32509, -32509, -32509,
227  24898, 23995, 22963, 21931, 20770, 19480, 18061, 16642,
228  15093, 13416, 11610, 9804, 7998, 6063, 3999, 1935,
229  0, -1935, -3999, -6063, -7998, -9804, -11610, -13416,
230  -15093, -16642, -18061, -19480, -20770, -21931, -22963, -23995
231 };
232 static const int16_t K2_table[] = {
233  0, -3096, -6321, -9417, -12513, -15351, -18061, -20770,
234  -23092, -25285, -27220, -28897, -30187, -31348, -32122, -32638,
235  0, 32638, 32122, 31348, 30187, 28897, 27220, 25285,
236  23092, 20770, 18061, 15351, 12513, 9417, 6321, 3096
237 };
238 static const int16_t K3_table[] = {
239  0, -3999, -8127, -12255, -16384, -20383, -24511, -28639,
240  32638, 28639, 24511, 20383, 16254, 12255, 8127, 3999
241 };
242 static const int16_t K5_table[] = {
243  0, -8127, -16384, -24511, 32638, 24511, 16254, 8127
244 };
245 
246 int VLM5030::Impl::getBits(unsigned sbit, unsigned bits)
247 {
248  unsigned offset = address + (sbit / 8);
249  unsigned data = (*rom)[(offset + 0) & address_mask] +
250  (*rom)[(offset + 1) & address_mask] * 256;
251  data >>= (sbit & 7);
252  data &= (0xFF >> (8 - bits));
253  return data;
254 }
255 
256 // get next frame
257 int VLM5030::Impl::parseFrame()
258 {
259  // remember previous frame
260  old_energy = new_energy;
261  old_pitch = new_pitch;
262  for (int i = 0; i <= 9; ++i) {
263  old_k[i] = new_k[i];
264  }
265  // command byte check
266  byte cmd = (*rom)[address & address_mask];
267  if (cmd & 0x01) {
268  // extend frame
269  new_energy = new_pitch = 0;
270  for (int i = 0; i <= 9; ++i) {
271  new_k[i] = 0;
272  }
273  ++address;
274  if (cmd & 0x02) {
275  // end of speech
276  return 0;
277  } else {
278  // silent frame
279  int nums = ((cmd >> 2) + 1) * 2;
280  return nums * FR_SIZE;
281  }
282  }
283  // pitch
284  new_pitch = (pitchtable[getBits(1, 5)] + pitch_offset) & 0xff;
285  // energy
286  new_energy = energytable[getBits(6, 5)];
287 
288  // 10 K's
289  new_k[9] = K5_table[getBits(11, 3)];
290  new_k[8] = K5_table[getBits(14, 3)];
291  new_k[7] = K5_table[getBits(17, 3)];
292  new_k[6] = K5_table[getBits(20, 3)];
293  new_k[5] = K5_table[getBits(23, 3)];
294  new_k[4] = K5_table[getBits(26, 3)];
295  new_k[3] = K3_table[getBits(29, 4)];
296  new_k[2] = K3_table[getBits(33, 4)];
297  new_k[1] = K2_table[getBits(37, 5)];
298  new_k[0] = K1_table[getBits(42, 6)];
299 
300  address += 6;
301  return FR_SIZE;
302 }
303 
304 // decode and buffering data
305 void VLM5030::Impl::generateChannels(int** bufs, unsigned length)
306 {
307  // Single channel device: replace content of bufs[0] (not add to it).
308  if (phase == PH_IDLE) {
309  bufs[0] = nullptr;
310  return;
311  }
312 
313  int buf_count = 0;
314 
315  // running
316  if (phase == PH_RUN || phase == PH_STOP) {
317  // playing speech
318  while (length > 0) {
319  int current_val;
320  // check new interpolator or new frame
321  if (sample_count == 0) {
322  if (phase == PH_STOP) {
323  phase = PH_END;
324  sample_count = 1;
325  goto phase_stop; // continue to end phase
326  }
327  sample_count = frame_size;
328  // interpolator changes
329  if (interp_count == 0) {
330  // change to new frame
331  interp_count = parseFrame(); // with change phase
332  if (interp_count == 0 ) {
333  // end mark found
334  interp_count = FR_SIZE;
335  sample_count = frame_size; // end -> stop time
336  phase = PH_STOP;
337  }
338  // Set old target as new start of frame
339  current_energy = old_energy;
340  current_pitch = old_pitch;
341  for (int i = 0; i <= 9; ++i) {
342  current_k[i] = old_k[i];
343  }
344  // is this a zero energy frame?
345  if (current_energy == 0) {
346  target_energy = 0;
347  target_pitch = current_pitch;
348  for (int i = 0; i <= 9; ++i) {
349  target_k[i] = current_k[i];
350  }
351  } else {
352  // normal frame
353  target_energy = new_energy;
354  target_pitch = new_pitch;
355  for (int i = 0; i <= 9; ++i) {
356  target_k[i] = new_k[i];
357  }
358  }
359  }
360  // next interpolator
361  // Update values based on step values 25%, 50%, 75%, 100%
362  interp_count -= interp_step;
363  // 3,2,1,0 -> 1,2,3,4
364  int interp_effect = FR_SIZE - (interp_count % FR_SIZE);
365  current_energy = old_energy + (target_energy - old_energy) * interp_effect / FR_SIZE;
366  if (old_pitch > 1) {
367  current_pitch = old_pitch + (target_pitch - old_pitch) * interp_effect / FR_SIZE;
368  }
369  for (int i = 0; i <= 9; ++i)
370  current_k[i] = old_k[i] + (target_k[i] - old_k[i]) * interp_effect / FR_SIZE;
371  }
372  // calcrate digital filter
373  if (old_energy == 0) {
374  // generate silent samples here
375  current_val = 0x00;
376  } else if (old_pitch <= 1) {
377  // generate unvoiced samples here
378  current_val = random_bool() ? int(current_energy)
379  : -int(current_energy);
380  } else {
381  // generate voiced samples here
382  current_val = (pitch_count == 0) ? current_energy : 0;
383  }
384 
385  // Lattice filter here
386  int u[11];
387  u[10] = current_val;
388  for (int i = 9; i >= 0; --i) {
389  u[i] = u[i + 1] - ((current_k[i] * x[i]) / 32768);
390  }
391  for (int i = 9; i >= 1; --i) {
392  x[i] = x[i - 1] + ((current_k[i - 1] * u[i - 1]) / 32768);
393  }
394  x[0] = u[0];
395 
396  // clipping, buffering
397  if (u[0] > 511) {
398  bufs[0][buf_count] = 511 << 6;
399  } else if (u[0] < -511) {
400  bufs[0][buf_count] = -511 << 6;
401  } else {
402  bufs[0][buf_count] = (u[0] << 6);
403  }
404  ++buf_count;
405  --sample_count;
406  ++pitch_count;
407  if (pitch_count >= current_pitch) {
408  pitch_count = 0;
409  }
410  --length;
411  }
412  // return;
413  }
414 phase_stop:
415  switch (phase) {
416  case PH_SETUP:
417  if (sample_count <= length) {
418  sample_count = 0;
419  // pin_BSY = true;
420  phase = PH_WAIT;
421  } else {
422  sample_count -= length;
423  }
424  break;
425  case PH_END:
426  if (sample_count <= length) {
427  sample_count = 0;
428  pin_BSY = false;
429  phase = PH_IDLE;
430  } else {
431  sample_count -= length;
432  }
433  }
434  // silent buffering
435  while (length > 0) {
436  bufs[0][buf_count++] = 0;
437  --length;
438  }
439 }
440 
441 // setup parameteroption when RST=H
442 void VLM5030::Impl::setupParameter(byte param)
443 {
444  // latch parameter value
445  parameter = param;
446 
447  // bit 0,1 : 4800bps / 9600bps , interporator step
448  if (param & 2) { // bit 1 = 1 , 9600bps
449  interp_step = 4; // 9600bps : no interporator
450  } else if (param & 1) { // bit1 = 0 & bit0 = 1 , 4800bps
451  interp_step = 2; // 4800bps : 2 interporator
452  } else { // bit1 = bit0 = 0 : 2400bps
453  interp_step = 1; // 2400bps : 4 interporator
454  }
455 
456  // bit 3,4,5 : speed (frame size)
457  frame_size = VLM5030_speed_table[(param >> 3) & 7];
458 
459  // bit 6,7 : low / high pitch
460  if (param & 0x80) { // bit7=1 , high pitch
461  pitch_offset = -8;
462  } else if (param & 0x40) { // bit6=1 , low pitch
463  pitch_offset = 8;
464  } else {
465  pitch_offset = 0;
466  }
467 }
468 
470 {
471  phase = PH_RESET;
472  address = 0;
473  vcu_addr_h = 0;
474  pin_BSY = false;
475 
476  old_energy = old_pitch = 0;
477  new_energy = new_pitch = 0;
478  current_energy = current_pitch = 0;
479  target_energy = target_pitch = 0;
480  memset(old_k, 0, sizeof(old_k));
481  memset(new_k, 0, sizeof(new_k));
482  memset(current_k, 0, sizeof(current_k));
483  memset(target_k, 0, sizeof(target_k));
484  interp_count = sample_count = pitch_count = 0;
485  memset(x, 0, sizeof(x));
486  // reset parameters
487  setupParameter(0x00);
488 }
489 
490 // get BSY pin level
492 {
493  const_cast<Impl*>(this)->updateStream(time);
494  return pin_BSY;
495 }
496 
497 // latch control data
499 {
500  latch_data = data;
501 }
502 
504 {
505  updateStream(time);
506  setRST((data & 0x01) != 0);
507  setVCU((data & 0x04) != 0);
508  setST ((data & 0x02) != 0);
509 }
510 
511 // set RST pin level : reset / set table address A8-A15
512 void VLM5030::Impl::setRST(bool pin)
513 {
514  if (pin_RST) {
515  if (!pin) { // H -> L : latch parameters
516  pin_RST = false;
517  setupParameter(latch_data);
518  }
519  } else {
520  if (pin) { // L -> H : reset chip
521  pin_RST = true;
522  if (pin_BSY) {
523  reset();
524  }
525  }
526  }
527 }
528 
529 // set VCU pin level : ?? unknown
530 void VLM5030::Impl::setVCU(bool pin)
531 {
532  // direct mode / indirect mode
533  pin_VCU = pin;
534 }
535 
536 // set ST pin level : set table address A0-A7 / start speech
537 void VLM5030::Impl::setST(bool pin)
538 {
539  if (pin_ST == pin) {
540  // pin level unchanged
541  return;
542  }
543  if (!pin) {
544  // H -> L
545  pin_ST = false;
546  if (pin_VCU) {
547  // direct access mode & address High
548  vcu_addr_h = (int(latch_data) << 8) + 0x01;
549  } else {
550  // check access mode
551  if (vcu_addr_h) {
552  // direct access mode
553  address = (vcu_addr_h & 0xff00) + latch_data;
554  vcu_addr_h = 0;
555  } else {
556  // indirect access mode
557  int table = (latch_data & 0xfe) + ((int(latch_data) & 1) << 8);
558  address = (((*rom)[(table + 0) & address_mask]) << 8) |
559  (*rom)[(table + 1) & address_mask];
560  }
561  // reset process status
562  sample_count = frame_size;
563  interp_count = FR_SIZE;
564  // clear filter
565  // start after 3 sampling cycle
566  phase = PH_RUN;
567  }
568  } else {
569  // L -> H
570  pin_ST = true;
571  // setup speech, BSY on after 30ms?
572  phase = PH_SETUP;
573  sample_count = 1; // wait time for busy on
574  pin_BSY = true;
575  }
576 }
577 
578 VLM5030::Impl::Impl(const std::string& name, const std::string& desc,
579  const std::string& romFilename, const DeviceConfig& config)
580  : ResampledSoundDevice(config.getMotherBoard(), name, desc, 1)
581 {
582  XMLElement voiceROMconfig(name);
583  voiceROMconfig.addAttribute("id", "name");
584  auto& romElement = voiceROMconfig.addChild("rom");
585  romElement.addChild( // load by sha1sum
586  "sha1", "4f36d139ee4baa7d5980f765de9895570ee05f40");
587  romElement.addChild( // load by predefined filename in software rom's dir
588  "filename", FileOperations::stripExtension(romFilename) + "_voice.rom");
589  romElement.addChild( // or hardcoded filename in ditto dir
590  "filename", "keyboardmaster/voice.rom");
591  rom = make_unique<Rom>(
592  name + " ROM", "rom", DeviceConfig(config, voiceROMconfig));
593 
594  // reset input pins
595  pin_RST = pin_ST = pin_VCU = false;
596  latch_data = 0;
597 
598  reset();
599  phase = PH_IDLE;
600 
601  address_mask = rom->getSize() - 1;
602 
603  const int CLOCK_FREQ = 3579545;
604  double input = CLOCK_FREQ / 440.0;
605  setInputRate(int(input + 0.5));
606 
607  registerSound(config);
608 }
609 
611 {
612  unregisterSound();
613 }
614 
615 template<typename Archive>
616 void VLM5030::Impl::serialize(Archive& ar, unsigned /*version*/)
617 {
618  ar.serialize("address_mask", address_mask);
619  ar.serialize("frame_size", frame_size);
620  ar.serialize("pitch_offset", pitch_offset);
621  ar.serialize("current_energy", current_energy);
622  ar.serialize("current_pitch", current_pitch);
623  ar.serialize("current_k", current_k);
624  ar.serialize("x", x);
625  ar.serialize("address", address);
626  ar.serialize("vcu_addr_h", vcu_addr_h);
627  ar.serialize("old_k", old_k);
628  ar.serialize("new_k", new_k);
629  ar.serialize("target_k", target_k);
630  ar.serialize("old_energy", old_energy);
631  ar.serialize("new_energy", new_energy);
632  ar.serialize("target_energy", target_energy);
633  ar.serialize("old_pitch", old_pitch);
634  ar.serialize("new_pitch", new_pitch);
635  ar.serialize("target_pitch", target_pitch);
636  ar.serialize("interp_step", interp_step);
637  ar.serialize("interp_count", interp_count);
638  ar.serialize("sample_count", sample_count);
639  ar.serialize("pitch_count", pitch_count);
640  ar.serialize("latch_data", latch_data);
641  ar.serialize("parameter", parameter);
642  ar.serialize("phase", phase);
643  ar.serialize("pin_BSY", pin_BSY);
644  ar.serialize("pin_ST", pin_ST);
645  ar.serialize("pin_VCU", pin_VCU);
646  ar.serialize("pin_RST", pin_RST);
647 }
648 
649 
650 // class VLM5030
651 
652 VLM5030::VLM5030(const std::string& name, const std::string& desc,
653  const std::string& romFilename, const DeviceConfig& config)
654  : pimpl(make_unique<Impl>(name, desc, romFilename, config))
655 {
656 }
657 
659 {
660 }
661 
663 {
664  pimpl->reset();
665 }
666 
668 {
669  pimpl->writeData(data);
670 }
671 
673 {
674  pimpl->writeControl(data, time);
675 }
676 
678 {
679  return pimpl->getBSY(time);
680 }
681 
682 template<typename Archive>
683 void VLM5030::serialize(Archive& ar, unsigned version)
684 {
685  pimpl->serialize(ar, version);
686 }
688 
689 } // namespace openmsx
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:281
Impl(const std::string &name, const std::string &desc, const std::string &romFilename, const DeviceConfig &config)
Definition: VLM5030.cc:578
void serialize(Archive &ar, unsigned version)
Definition: VLM5030.cc:683
void writeControl(byte data, EmuTime::param time)
set RST / VCU / ST pins
Definition: VLM5030.cc:672
void writeData(byte data)
Definition: VLM5030.cc:498
void writeControl(byte data, EmuTime::param time)
Definition: VLM5030.cc:503
void writeData(byte data)
latch control data
Definition: VLM5030.cc:667
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:79
bool getBSY(EmuTime::param time) const
get BSY pin level
Definition: VLM5030.cc:677
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:25
bool random_bool()
Return a random boolean value.
Definition: random.hh:24
string_ref stripExtension(string_ref path)
Returns the path without extension.
VLM5030(const std::string &name, const std::string &desc, const std::string &romFilename, const DeviceConfig &config)
Definition: VLM5030.cc:652
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
void addAttribute(string_ref name, string_ref value)
Definition: XMLElement.cc:57
XMLElement & addChild(string_ref name)
Definition: XMLElement.cc:29
void serialize(Archive &ar, unsigned version)
Definition: VLM5030.cc:616
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
bool getBSY(EmuTime::param time) const
Definition: VLM5030.cc:491
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:81
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:28