1 /** 2 Copyright: Copyright (c) 2013-2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 8 module emulator.dcpu.devices.lem1802; 9 10 import std.stdio; 11 12 import anchovy.graphics.bitmap; 13 14 import emulator.dcpu.devices.idevice; 15 import emulator.dcpu.emulator; 16 import emulator.dcpu.dcpu; 17 import emulator.utils.undoproxy; 18 19 @trusted nothrow: 20 21 /++ 22 + NE_LEM1802 v1.0 23 + Low Energy Monitor 24 + See 'docs/LEM1802 monitor.txt' for specification. 25 +/ 26 27 private struct Lem1802Registers 28 { 29 ushort fontAddress; 30 ushort videoAddress; 31 ushort paletteAddress; 32 ushort borderColor; 33 34 bool blinkPhase; 35 bool enabled = false; 36 bool splash = false; 37 } 38 39 class Lem1802(Cpu) : IDevice!Cpu 40 { 41 protected: 42 Emulator!Cpu _emulator; 43 Cpu* _dcpu; 44 Bitmap _bitmap; 45 46 UndoableStruct!(Lem1802Registers, ushort) regs; 47 48 bool showSplash = true; 49 uint blinkInterval = 70000; 50 uint splashDelay = 50000; 51 52 enum numRows = 12; 53 enum numCols = 32; 54 enum charWidth = 4; 55 enum charHeight = 8; 56 enum borderSize = 4; 57 enum screenWidth = numCols * charWidth + borderSize * 2; 58 enum screenHeight = numRows * charHeight + borderSize * 2; 59 60 public: 61 this() 62 { 63 _bitmap = new Bitmap(screenWidth, screenHeight, 4); 64 } 65 66 Bitmap bitmap() @property 67 { 68 return _bitmap; 69 } 70 71 override void attachEmulator(Emulator!Cpu emulator) 72 { 73 _emulator = emulator; 74 _dcpu = &emulator.dcpu; 75 (cast(uint[])_bitmap.data)[] = 0xFF000000; 76 regs.enabled = false; 77 regs.splash = false; 78 regs.fontAddress = 0; 79 regs.videoAddress = 0; 80 regs.paletteAddress = 0; 81 regs.borderColor = 0; 82 83 _bitmap.dataChanged.emit(); 84 } 85 86 /// Handles hardware interrupt and returns a number of cycles. 87 override uint handleInterrupt() 88 { 89 ushort aRegister = _emulator.dcpu.regs.a; 90 ushort bRegister = _emulator.dcpu.regs.b; 91 92 switch(aRegister) 93 { 94 case 0: 95 mapScreen(bRegister); 96 return 0; 97 case 1: 98 mapFont(bRegister); 99 return 0; 100 case 2: 101 mapPalette(bRegister); 102 return 0; 103 case 3: 104 setBorderColor(bRegister); 105 return 0; 106 case 4: 107 dumpFont(bRegister); 108 return 256; 109 case 5: 110 dumpPalette(bRegister); 111 return 16; 112 default: 113 break; 114 } 115 116 return 0; 117 } 118 119 /// Called every application frame. 120 /// Can be used to update screens. 121 override void updateFrame() 122 { 123 if (regs.enabled) 124 { 125 if (regs.splash && showSplash) 126 drawSplash(); 127 else 128 repaintScreen(); 129 } 130 else 131 clearScreen(); 132 } 133 134 override void handleUpdateQuery(ref ulong delay) 135 { 136 if (regs.splash) // remove splash 137 { 138 delay = blinkInterval; 139 regs.splash = false; 140 } 141 else 142 { 143 regs.blinkPhase = !regs.blinkPhase; 144 delay = blinkInterval; 145 } 146 } 147 148 /// Returns: 32 bit word identifying the hardware id. 149 override uint hardwareId() @property 150 { 151 return 0x7349f615; 152 } 153 154 /// Returns: 16 bit word identifying the hardware version. 155 override ushort hardwareVersion() @property 156 { 157 return 0x1802; 158 } 159 160 /// Returns: 32 bit word identifying the manufacturer 161 override uint manufacturer() @property 162 { 163 return 0x1c6c8b36; 164 } 165 166 override void commitFrame(ulong frameNumber) 167 { 168 regs.commitFrame(frameNumber); 169 } 170 171 override void discardFrame() 172 { 173 regs.discardFrame(); 174 } 175 176 override void undoFrames(ulong numFrames) 177 { 178 regs.undoFrames(numFrames); 179 } 180 181 override void discardUndoStack() 182 { 183 regs.discardUndoStack(); 184 } 185 186 override size_t undoStackSize() @property 187 { 188 return regs.undoStackSize; 189 } 190 191 protected: 192 193 void repaintScreen() 194 { 195 if (_dcpu is null || regs.videoAddress == 0) return; 196 197 foreach(line; 0..numRows) 198 { 199 foreach(column; 0..numCols) 200 { 201 ushort memoryAddress = (regs.videoAddress + line * numCols + column) & 0xFFFF; 202 drawChar(_dcpu.mem[memoryAddress], column, line); 203 } 204 } 205 206 _bitmap.dataChanged.emit(); 207 } 208 209 void clearScreen() 210 { 211 (cast(uint[])_bitmap.data)[] = 0xFF000000; 212 _bitmap.dataChanged.emit(); 213 } 214 215 void drawChar(ushort charData, size_t x, size_t y) 216 { 217 uint charIndex = charData & 0x7F; 218 bool blinkBit = (charData & 0x80) > 0; 219 ushort foreIndex = (charData & 0xF000) >> 12; 220 ushort backIndex = (charData & 0xF00) >> 8; 221 222 uint foreRGB = paletteToRGB8(foreIndex); 223 uint backRGB = paletteToRGB8(backIndex); 224 225 if (blinkBit && regs.blinkPhase) 226 { 227 fillCell(x, y, backRGB); 228 } 229 else if (regs.fontAddress == 0) 230 { 231 drawCell(x, y, foreRGB, backRGB, (cast(uint[])defaultFont)[charIndex]); 232 } 233 else 234 { 235 drawCell(x, y, foreRGB, backRGB, _dcpu.mem[(regs.fontAddress + charIndex*2) & 0xFFFF] | 236 _dcpu.mem[(regs.fontAddress + charIndex*2 + 1) & 0xFFFF] << 16); 237 } 238 239 drawBorder(); 240 } 241 242 uint paletteToRGB8(ushort colorIndex) 243 { 244 ushort rgb12color; 245 if (regs.paletteAddress == 0) 246 { 247 rgb12color = defaultPalette[colorIndex & 0xF]; 248 } 249 else 250 { 251 rgb12color = _dcpu.mem[(regs.paletteAddress + (colorIndex & 0xF)) & 0xFFFF]; 252 } 253 254 return ((rgb12color & 0xF) << 16) * 17 + 255 ((rgb12color & 0xF0) << 4) * 17 + 256 ((rgb12color & 0xF00) >> 8) * 17 + 257 0xFF000000; 258 } 259 260 void fillCell(size_t x, size_t y, uint color) 261 { 262 uint cellX = borderSize + x * charWidth; 263 uint cellY = borderSize + y * charHeight; 264 265 uint[] data = cast(uint[])_bitmap.data; 266 267 size_t dataPos; 268 uint[] cellLine; 269 270 foreach(i; 0..8) 271 { 272 dataPos = cellX + screenWidth * (cellY + i); 273 cellLine = data[dataPos .. dataPos + 4]; 274 cellLine[] = color; 275 } 276 } 277 278 void drawCell(size_t x, size_t y, uint foreColor, uint backColor, uint charData) 279 { 280 uint cellX = borderSize + x * charWidth; 281 uint cellY = borderSize + y * charHeight; 282 283 uint[] colorData = cast(uint[])_bitmap.data; 284 285 size_t dataPos; 286 uint[] cellLine; 287 288 foreach(i; 0..8) 289 { 290 dataPos = cellX + screenWidth * (cellY + i); 291 cellLine = colorData[dataPos .. dataPos + 4]; 292 293 cellLine[0] = charData & (1 << (i + 8)) ? foreColor : backColor; 294 cellLine[1] = charData & (1 << (i + 0)) ? foreColor : backColor; 295 cellLine[2] = charData & (1 << (i + 24)) ? foreColor : backColor; 296 cellLine[3] = charData & (1 << (i + 16)) ? foreColor : backColor; 297 } 298 } 299 300 void drawBorder() 301 { 302 uint borderRgb = paletteToRGB8(regs.borderColor); 303 uint[] colorData = cast(uint[])_bitmap.data; 304 305 colorData[0..borderSize * screenWidth][] = borderRgb; 306 307 size_t topOffset; 308 foreach(line; borderSize..screenHeight - borderSize) 309 { 310 topOffset = line * screenWidth; 311 colorData[topOffset..topOffset + borderSize][] = borderRgb; 312 colorData[topOffset + screenWidth - borderSize .. topOffset + screenWidth][] = borderRgb; 313 } 314 315 colorData[$-borderSize * screenWidth..$][] = borderRgb; 316 } 317 318 void drawSplash() 319 { 320 import std.bitmanip; 321 (cast(uint[])_bitmap.data)[] = splashBackRgb; 322 323 BitArray array; 324 array.init(cast(void[])splashImage, splashImage.length * 16); 325 326 foreach(line; 0..splashHeight) 327 { 328 foreach(col; 0.. splashWidth) 329 { 330 (cast(uint[])_bitmap.data)[(splashY+line+borderSize) * bitmap.size.x + splashX + col + borderSize] = 331 array[line * splashWidth + col] ? splashForeRgb : splashBackRgb; 332 } 333 } 334 335 _bitmap.dataChanged.emit(); 336 } 337 338 void mapScreen(ushort b) 339 { 340 if (b != 0 && regs.videoAddress == 0) 341 { 342 regs.splash = true; 343 regs.enabled = true; 344 345 drawSplash(); 346 347 _dcpu.updateQueue.addQuery(this, splashDelay); 348 } 349 else if (b == 0) 350 { 351 (cast(uint[])_bitmap.data)[] = 0xFF000000; 352 _dcpu.updateQueue.removeQueries(this); 353 354 regs.enabled = false; 355 } 356 357 regs.videoAddress = b; 358 } 359 360 void mapFont(ushort b) 361 { 362 regs.fontAddress = b; 363 } 364 365 void mapPalette(ushort b) 366 { 367 regs.paletteAddress = b; 368 } 369 370 void setBorderColor(ushort b) 371 { 372 regs.borderColor = b & 0xF; 373 } 374 375 void dumpFont(ushort b) 376 { 377 ushort pointer = b; 378 379 foreach(word; cast(ushort[])defaultFont) 380 { 381 _dcpu.mem[pointer] = word; 382 ++pointer; 383 } 384 } 385 386 void dumpPalette(ushort b) 387 { 388 ushort pointer = b; 389 390 foreach(word; defaultPalette) 391 { 392 _dcpu.mem[pointer] = word; 393 ++pointer; 394 } 395 } 396 } 397 398 static immutable ushort[] defaultPalette = [ 399 0x000, 0x00a, 0x0a0, 0x0aa, 400 0xa00, 0xa0a, 0xa50, 0xaaa, 401 0x555, 0x55f, 0x5f5, 0x5ff, 402 0xf55, 0xf5f, 0xff5, 0xfff 403 ]; 404 405 static immutable ushort[] defaultFont = [ 406 0xb79e, 0x388e, 0x722c, 0x75f4, 0x19bb, 0x7f8f, 0x85f9, 0xb158, 407 0x242e, 0x2400, 0x082a, 0x0800, 0x0008, 0x0000, 0x0808, 0x0808, 408 0x00ff, 0x0000, 0x00f8, 0x0808, 0x08f8, 0x0000, 0x080f, 0x0000, 409 0x000f, 0x0808, 0x00ff, 0x0808, 0x08f8, 0x0808, 0x08ff, 0x0000, 410 0x080f, 0x0808, 0x08ff, 0x0808, 0x6633, 0x99cc, 0x9933, 0x66cc, 411 0xfef8, 0xe080, 0x7f1f, 0x0701, 0x0107, 0x1f7f, 0x80e0, 0xf8fe, 412 0x5500, 0xaa00, 0x55aa, 0x55aa, 0xffaa, 0xff55, 0x0f0f, 0x0f0f, 413 0xf0f0, 0xf0f0, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 414 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00, 415 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100, 416 0x1c22, 0x4100, 0x4122, 0x1c00, 0x1408, 0x1400, 0x081c, 0x0800, 417 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300, 418 0x3e49, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600, 419 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700, 420 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000, 421 0x0814, 0x2200, 0x1414, 0x1400, 0x2214, 0x0800, 0x0259, 0x0600, 422 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200, 423 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e41, 0x7a00, 424 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f08, 0x7700, 425 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00, 426 0x7f09, 0x0600, 0x3e61, 0x7e00, 0x7f09, 0x7600, 0x2649, 0x3200, 427 0x017f, 0x0100, 0x3f40, 0x7f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00, 428 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100, 429 0x031c, 0x6000, 0x417f, 0x0000, 0x0201, 0x0200, 0x8080, 0x8000, 430 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800, 431 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00, 432 0x7f04, 0x7800, 0x047d, 0x0000, 0x2040, 0x3d00, 0x7f10, 0x6c00, 433 0x017f, 0x0000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800, 434 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400, 435 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00, 436 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100, 437 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x0205, 0x0200 438 ]; 439 440 // 1bit image. 1 - splashForeRgb, 0 - splashBackRgb. 441 // only center piece 52 x 36. 442 static immutable ushort[] splashImage = [ 443 0x6000, 0x0180, 0x0000, 0x0000, 0x1806, 0x0000, 0x0000, 0x80e0, 0x0001, 0x0000, 444 0x0c00, 0x0018, 0x0000, 0xc000, 0x0181, 0x0000, 0x3000, 0x1818, 0x0000, 0x0000, 445 0x8383, 0x0001, 0x0000, 0x3070, 0x0018, 0x0000, 0x0700, 0x8187, 0x0fff, 0xf000, 446 0x1860, 0xfff8, 0x0000, 0x8e0f, 0x0001, 0x0000, 0xc1f0, 0x0018, 0x0000, 0x1b00, 447 0x019c, 0x0000, 0xb000, 0x1983, 0x0000, 0x0000, 0xb833, 0x0001, 0x0000, 0x0730, 448 0x001b, 0x0000, 0x6300, 0x01f0, 0x0000, 0x3000, 0x1e0e, 0x0000, 0x0000, 0xe0c3, 449 0xff81, 0x000f, 0x1c30, 0xf81c, 0x00ff, 0x8300, 0x01c1, 0x0000, 0x3000, 0x1838, 450 0x0000, 0x0000, 0x8303, 0x0001, 0x0000, 0x7030, 0x0000, 0x0000, 0x0300, 0x0006, 451 0x0000, 0x3000, 0x00e0, 0x0000, 0x0000, 0x0c03, 0x0000, 0x0000, 0xc030, 0x0000, 452 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 453 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6530, 0xb8b8, 0xcbba, 454 0x75ca, 0x8987, 0x9919, 0x5e64, 0xb852, 0x92bb, 0xaa6a, 0x0000, 455 ]; 456 457 enum splashBackRgb = 0xFFAA0000; 458 enum splashForeRgb = 0xFF00FFFF; 459 460 enum splashWidth = 52; 461 enum splashHeight = 36; 462 463 enum splashX = 38; 464 enum splashY = 25;