pl111.cc revision 7823:dac01f14f20f
1/*
2 * Copyright (c) 2010 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: William Wang
38 */
39
40#include "base/trace.hh"
41#include "dev/arm/amba_device.hh"
42#include "dev/arm/gic.hh"
43#include "dev/arm/pl111.hh"
44#include "mem/packet.hh"
45#include "mem/packet_access.hh"
46
47using namespace AmbaDev;
48
49// initialize clcd registers
50Pl111::Pl111(const Params *p)
51    : AmbaDmaDevice(p), lcdTiming0(0), lcdTiming1(0), lcdTiming2(0),
52      lcdTiming3(0), lcdUpbase(0), lcdLpbase(0), lcdControl(0), lcdImsc(0),
53      lcdRis(0), lcdMis(0), lcdIcr(0), lcdUpcurr(0), lcdLpcurr(0),
54      clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0),
55      clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0),
56      clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0), clock(p->clock),
57      height(0), width(0), startTime(0), startAddr(0), maxAddr(0), curAddr(0),
58      waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this),
59      dmaDoneEvent(maxOutstandingDma, this), intEvent(this)
60{
61    pioSize = 0xFFFF;
62
63    memset(lcdPalette, 0, sizeof(lcdPalette));
64    memset(cursorImage, 0, sizeof(cursorImage));
65    memset(dmaBuffer, 0, sizeof(dmaBuffer));
66    memset(frameBuffer, 0, sizeof(frameBuffer));
67}
68
69// read registers and frame buffer
70Tick
71Pl111::read(PacketPtr pkt)
72{
73    // use a temporary data since the LCD registers are read/written with
74    // different size operations
75
76    uint32_t data = 0;
77
78    if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
79
80        assert(pkt->getAddr() >= pioAddr &&
81               pkt->getAddr() < pioAddr + pioSize);
82
83        Addr daddr = pkt->getAddr()&0xFFFF;
84        pkt->allocate();
85
86        DPRINTF(PL111, " read register %#x size=%d\n", daddr, pkt->getSize());
87
88        switch (daddr) {
89          case LcdTiming0:
90            data = lcdTiming0;
91            break;
92          case LcdTiming1:
93            data = lcdTiming1;
94            break;
95          case LcdTiming2:
96            data = lcdTiming2;
97            break;
98          case LcdTiming3:
99            data = lcdTiming3;
100            break;
101          case LcdUpBase:
102            data = lcdUpbase;
103            break;
104          case LcdLpBase:
105            data = lcdLpbase;
106            break;
107          case LcdControl:
108            data = lcdControl;
109            break;
110          case LcdImsc:
111            warn("LCD interrupt set/clear function not supported\n");
112            data = lcdImsc;
113            break;
114          case LcdRis:
115            warn("LCD Raw interrupt status function not supported\n");
116            data = lcdRis;
117            break;
118          case LcdMis:
119            warn("LCD Masked interrupt status function not supported\n");
120            data = lcdMis;
121            break;
122          case LcdIcr:
123            panic("LCD register at offset %#x is Write-Only\n", daddr);
124            break;
125          case LcdUpCurr:
126            data = lcdUpcurr;
127            break;
128          case LcdLpCurr:
129            data = lcdLpcurr;
130            break;
131          case ClcdCrsrCtrl:
132            data = clcdCrsrCtrl;
133            break;
134          case ClcdCrsrConfig:
135            data = clcdCrsrConfig;
136            break;
137          case ClcdCrsrPalette0:
138            data = clcdCrsrPalette0;
139            break;
140          case ClcdCrsrPalette1:
141            data = clcdCrsrPalette1;
142            break;
143          case ClcdCrsrXY:
144            data = clcdCrsrXY;
145            break;
146          case ClcdCrsrClip:
147            data = clcdCrsrClip;
148            break;
149          case ClcdCrsrImsc:
150            data = clcdCrsrImsc;
151            break;
152          case ClcdCrsrIcr:
153            panic("CLCD register at offset %#x is Write-Only\n", daddr);
154            break;
155          case ClcdCrsrRis:
156            data = clcdCrsrRis;
157            break;
158          case ClcdCrsrMis:
159            data = clcdCrsrMis;
160            break;
161          default:
162            if (AmbaDev::readId(pkt, AMBA_ID, pioAddr)) {
163                // Hack for variable size accesses
164                data = pkt->get<uint32_t>();
165                break;
166            } else if (daddr >= CrsrImage && daddr <= 0xBFC) {
167                // CURSOR IMAGE
168                int index;
169                index = (daddr - CrsrImage) >> 2;
170                data= cursorImage[index];
171                break;
172            } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
173                // LCD Palette
174                int index;
175                index = (daddr - LcdPalette) >> 2;
176                data = lcdPalette[index];
177                break;
178            } else {
179                panic("Tried to read CLCD register at offset %#x that \
180                       doesn't exist\n", daddr);
181                break;
182            }
183        }
184    }
185
186    switch(pkt->getSize()) {
187      case 1:
188        pkt->set<uint8_t>(data);
189        break;
190      case 2:
191        pkt->set<uint16_t>(data);
192        break;
193      case 4:
194        pkt->set<uint32_t>(data);
195        break;
196      default:
197        panic("CLCD controller read size too big?\n");
198        break;
199    }
200
201    pkt->makeAtomicResponse();
202    return pioDelay;
203}
204
205// write registers and frame buffer
206Tick
207Pl111::write(PacketPtr pkt)
208{
209    // use a temporary data since the LCD registers are read/written with
210    // different size operations
211    //
212    uint32_t data = 0;
213
214    switch(pkt->getSize()) {
215      case 1:
216        data = pkt->get<uint8_t>();
217        break;
218      case 2:
219        data = pkt->get<uint16_t>();
220        break;
221      case 4:
222        data = pkt->get<uint32_t>();
223        break;
224      default:
225        panic("PL111 CLCD controller write size too big?\n");
226        break;
227    }
228
229    if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
230
231        assert(pkt->getAddr() >= pioAddr &&
232               pkt->getAddr() < pioAddr + pioSize);
233
234        Addr daddr = pkt->getAddr() - pioAddr;
235
236        DPRINTF(PL111, " write register %#x value %#x size=%d\n", daddr,
237                pkt->get<uint8_t>(), pkt->getSize());
238
239        switch (daddr) {
240          case LcdTiming0:
241            lcdTiming0 = data;
242            // width = 16 * (PPL+1)
243            width = (lcdTiming0.ppl + 1) << 4;
244            break;
245          case LcdTiming1:
246            lcdTiming1 = data;
247            // height = LPP + 1
248            height  = (lcdTiming1.lpp) + 1;
249            break;
250          case LcdTiming2:
251            lcdTiming2 = data;
252            break;
253          case LcdTiming3:
254            lcdTiming3 = data;
255            break;
256          case LcdUpBase:
257            lcdUpbase  = data;
258            break;
259          case LcdLpBase:
260            warn("LCD dual screen mode not supported\n");
261            lcdLpbase  = data;
262            break;
263          case LcdControl:
264            int old_lcdpwr;
265            old_lcdpwr = lcdControl.lcdpwr;
266            lcdControl = data;
267            // LCD power enable
268            if (lcdControl.lcdpwr&&!old_lcdpwr) {
269                DPRINTF(PL111, " lcd size: height %d width %d\n", height, width);
270                waterMark = lcdControl.watermark ? 8 : 4;
271                readFramebuffer();
272            }
273            break;
274          case LcdImsc:
275            warn("LCD interrupt mask set/clear not supported\n");
276            lcdImsc    = data;
277            break;
278          case LcdRis:
279            warn("LCD register at offset %#x is Read-Only\n", daddr);
280            break;
281          case LcdMis:
282            warn("LCD register at offset %#x is Read-Only\n", daddr);
283            break;
284          case LcdIcr:
285            warn("LCD interrupt clear not supported\n");
286            lcdIcr     = data;
287            break;
288          case LcdUpCurr:
289            warn("LCD register at offset %#x is Read-Only\n", daddr);
290            break;
291          case LcdLpCurr:
292            warn("LCD register at offset %#x is Read-Only\n", daddr);
293            break;
294          case ClcdCrsrCtrl:
295            clcdCrsrCtrl = data;
296            break;
297          case ClcdCrsrConfig:
298            clcdCrsrConfig = data;
299            break;
300          case ClcdCrsrPalette0:
301            clcdCrsrPalette0 = data;
302            break;
303          case ClcdCrsrPalette1:
304            clcdCrsrPalette1 = data;
305            break;
306          case ClcdCrsrXY:
307            clcdCrsrXY = data;
308            break;
309          case ClcdCrsrClip:
310            clcdCrsrClip = data;
311            break;
312          case ClcdCrsrImsc:
313            clcdCrsrImsc = data;
314            break;
315          case ClcdCrsrIcr:
316            clcdCrsrIcr = data;
317            break;
318          case ClcdCrsrRis:
319            warn("CLCD register at offset %#x is Read-Only\n", daddr);
320            break;
321          case ClcdCrsrMis:
322            warn("CLCD register at offset %#x is Read-Only\n", daddr);
323            break;
324          default:
325            if (daddr >= CrsrImage && daddr <= 0xBFC) {
326                // CURSOR IMAGE
327                int index;
328                index = (daddr - CrsrImage) >> 2;
329                cursorImage[index] = data;
330                break;
331            } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
332                // LCD Palette
333                int index;
334                index = (daddr - LcdPalette) >> 2;
335                lcdPalette[index] = data;
336                break;
337            } else {
338                panic("Tried to write PL111 register at offset %#x that \
339                       doesn't exist\n", daddr);
340                break;
341            }
342        }
343    }
344
345    pkt->makeAtomicResponse();
346    return pioDelay;
347}
348
349void
350Pl111::readFramebuffer()
351{
352    // initialization for dma read from frame buffer to dma buffer
353    uint32_t length  = height*width;
354    if (startAddr != lcdUpbase) {
355        startAddr = lcdUpbase;
356    }
357    curAddr = 0;
358    startTime = curTick();
359    maxAddr = static_cast<Addr>(length*sizeof(uint32_t));
360    dmaPendingNum =0 ;
361
362    fillFifo();
363}
364
365void
366Pl111::fillFifo()
367{
368    while ((dmaPendingNum < maxOutstandingDma) && (maxAddr >= curAddr + dmaSize )) {
369        // concurrent dma reads need different dma done events
370        // due to assertion in scheduling state
371        ++dmaPendingNum;
372        DPRINTF(PL111, " ++ DMA pending number %d read addr %#x\n",
373                dmaPendingNum, curAddr);
374        assert(!dmaDoneEvent[dmaPendingNum-1].scheduled());
375        dmaRead(curAddr + startAddr, dmaSize, &dmaDoneEvent[dmaPendingNum-1],
376                curAddr + dmaBuffer);
377        curAddr += dmaSize;
378    }
379}
380
381void
382Pl111::dmaDone()
383{
384    Tick maxFrameTime = lcdTiming2.cpl*height*clock;
385
386    --dmaPendingNum;
387
388    DPRINTF(PL111, " -- DMA pending number %d\n", dmaPendingNum);
389
390    if (maxAddr == curAddr && !dmaPendingNum) {
391        if ((curTick() - startTime) > maxFrameTime)
392            warn("CLCD controller buffer underrun, took %d cycles when should"
393                 " have taken %d\n", curTick() - startTime, maxFrameTime);
394
395        // double buffering so the vnc server doesn't see a tear in the screen
396        memcpy(frameBuffer, dmaBuffer, maxAddr);
397        assert(!readEvent.scheduled());
398
399        DPRINTF(PL111, "-- write out frame buffer into bmp\n");
400        writeBMP(frameBuffer);
401
402        DPRINTF(PL111, "-- schedule next dma read event at %d tick \n",
403                maxFrameTime + curTick());
404        schedule(readEvent, nextCycle(startTime + maxFrameTime));
405    }
406
407    if (dmaPendingNum > (maxOutstandingDma - waterMark))
408        return;
409
410    if (!fillFifoEvent.scheduled())
411        schedule(fillFifoEvent, nextCycle());
412
413}
414
415Tick
416Pl111::nextCycle()
417{
418    Tick nextTick = curTick() + clock - 1;
419    nextTick -= nextTick%clock;
420    return nextTick;
421}
422
423Tick
424Pl111::nextCycle(Tick beginTick)
425{
426    Tick nextTick = beginTick;
427    if (nextTick%clock!=0)
428        nextTick = nextTick - (nextTick%clock) + clock;
429
430    assert(nextTick >= curTick());
431    return nextTick;
432}
433
434// write out the frame buffer into a bitmap file
435void
436Pl111::writeBMP(uint32_t* frameBuffer)
437{
438    fstream pic;
439
440    // write out bmp head
441    std::string filename = "./m5out/frameBuffer.bmp";
442    pic.open(filename.c_str(), ios::out|ios::binary);
443    Bitmap bm(pic, height, width);
444
445    DPRINTF(PL111, "-- write out data into bmp\n");
446
447    // write out frame buffer data
448    for (int i = height -1; i >= 0; --i) {
449        for (int j = 0; j< width; ++j) {
450            uint32_t pixel = frameBuffer[i*width + j];
451            pic.write(reinterpret_cast<char*>(&pixel),
452                      sizeof(uint32_t));
453            DPRINTF(PL111, " write pixel data  %#x at addr %#x\n",
454                    pixel, i*width + j);
455        }
456    }
457
458    pic.close();
459}
460
461void
462Pl111::serialize(std::ostream &os)
463{
464    DPRINTF(PL111, "Serializing ARM PL111\n");
465
466    uint32_t lcdTiming0_serial = lcdTiming0;
467    SERIALIZE_SCALAR(lcdTiming0_serial);
468
469    uint32_t lcdTiming1_serial = lcdTiming1;
470    SERIALIZE_SCALAR(lcdTiming1_serial);
471
472    uint32_t lcdTiming2_serial = lcdTiming2;
473    SERIALIZE_SCALAR(lcdTiming2_serial);
474
475    uint32_t lcdTiming3_serial = lcdTiming3;
476    SERIALIZE_SCALAR(lcdTiming3_serial);
477
478    SERIALIZE_SCALAR(lcdUpbase);
479    SERIALIZE_SCALAR(lcdLpbase);
480
481    uint32_t lcdControl_serial = lcdControl;
482    SERIALIZE_SCALAR(lcdControl_serial);
483
484    uint8_t lcdImsc_serial = lcdImsc;
485    SERIALIZE_SCALAR(lcdImsc_serial);
486
487    uint8_t lcdRis_serial = lcdRis;
488    SERIALIZE_SCALAR(lcdRis_serial);
489
490    uint8_t lcdMis_serial = lcdMis;
491    SERIALIZE_SCALAR(lcdMis_serial);
492
493    uint8_t lcdIcr_serial = lcdIcr;
494    SERIALIZE_SCALAR(lcdIcr_serial);
495
496    SERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
497    SERIALIZE_ARRAY(cursorImage, CrsrImageSize);
498
499    SERIALIZE_SCALAR(clcdCrsrCtrl);
500    SERIALIZE_SCALAR(clcdCrsrConfig);
501    SERIALIZE_SCALAR(clcdCrsrPalette0);
502    SERIALIZE_SCALAR(clcdCrsrPalette1);
503    SERIALIZE_SCALAR(clcdCrsrXY);
504    SERIALIZE_SCALAR(clcdCrsrClip);
505
506    uint8_t clcdCrsrImsc_serial = clcdCrsrImsc;
507    SERIALIZE_SCALAR(clcdCrsrImsc_serial);
508
509    uint8_t clcdCrsrIcr_serial = clcdCrsrIcr;
510    SERIALIZE_SCALAR(clcdCrsrIcr_serial);
511
512    uint8_t clcdCrsrRis_serial = clcdCrsrRis;
513    SERIALIZE_SCALAR(clcdCrsrRis_serial);
514
515    uint8_t clcdCrsrMis_serial = clcdCrsrMis;
516    SERIALIZE_SCALAR(clcdCrsrMis_serial);
517
518    SERIALIZE_SCALAR(clock);
519    SERIALIZE_SCALAR(height);
520    SERIALIZE_SCALAR(width);
521
522    SERIALIZE_ARRAY(dmaBuffer, height*width);
523    SERIALIZE_ARRAY(frameBuffer, height*width);
524    SERIALIZE_SCALAR(startTime);
525    SERIALIZE_SCALAR(startAddr);
526    SERIALIZE_SCALAR(maxAddr);
527    SERIALIZE_SCALAR(curAddr);
528    SERIALIZE_SCALAR(waterMark);
529    SERIALIZE_SCALAR(dmaPendingNum);
530}
531
532void
533Pl111::unserialize(Checkpoint *cp, const std::string &section)
534{
535    DPRINTF(PL111, "Unserializing ARM PL111\n");
536
537    uint32_t lcdTiming0_serial;
538    UNSERIALIZE_SCALAR(lcdTiming0_serial);
539    lcdTiming0 = lcdTiming0_serial;
540
541    uint32_t lcdTiming1_serial;
542    UNSERIALIZE_SCALAR(lcdTiming1_serial);
543    lcdTiming1 = lcdTiming1_serial;
544
545    uint32_t lcdTiming2_serial;
546    UNSERIALIZE_SCALAR(lcdTiming2_serial);
547    lcdTiming2 = lcdTiming2_serial;
548
549    uint32_t lcdTiming3_serial;
550    UNSERIALIZE_SCALAR(lcdTiming3_serial);
551    lcdTiming3 = lcdTiming3_serial;
552
553    UNSERIALIZE_SCALAR(lcdUpbase);
554    UNSERIALIZE_SCALAR(lcdLpbase);
555
556    uint32_t lcdControl_serial;
557    UNSERIALIZE_SCALAR(lcdControl_serial);
558    lcdControl = lcdControl_serial;
559
560    uint8_t lcdImsc_serial;
561    UNSERIALIZE_SCALAR(lcdImsc_serial);
562    lcdImsc = lcdImsc_serial;
563
564    uint8_t lcdRis_serial;
565    UNSERIALIZE_SCALAR(lcdRis_serial);
566    lcdRis = lcdRis_serial;
567
568    uint8_t lcdMis_serial;
569    UNSERIALIZE_SCALAR(lcdMis_serial);
570    lcdMis = lcdMis_serial;
571
572    uint8_t lcdIcr_serial;
573    UNSERIALIZE_SCALAR(lcdIcr_serial);
574    lcdIcr = lcdIcr_serial;
575
576    UNSERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
577    UNSERIALIZE_ARRAY(cursorImage, CrsrImageSize);
578
579    UNSERIALIZE_SCALAR(clcdCrsrCtrl);
580    UNSERIALIZE_SCALAR(clcdCrsrConfig);
581    UNSERIALIZE_SCALAR(clcdCrsrPalette0);
582    UNSERIALIZE_SCALAR(clcdCrsrPalette1);
583    UNSERIALIZE_SCALAR(clcdCrsrXY);
584    UNSERIALIZE_SCALAR(clcdCrsrClip);
585
586    uint8_t clcdCrsrImsc_serial;
587    UNSERIALIZE_SCALAR(clcdCrsrImsc_serial);
588    clcdCrsrImsc = clcdCrsrImsc_serial;
589
590    uint8_t clcdCrsrIcr_serial;
591    UNSERIALIZE_SCALAR(clcdCrsrIcr_serial);
592    clcdCrsrIcr = clcdCrsrIcr_serial;
593
594    uint8_t clcdCrsrRis_serial;
595    UNSERIALIZE_SCALAR(clcdCrsrRis_serial);
596    clcdCrsrRis = clcdCrsrRis_serial;
597
598    uint8_t clcdCrsrMis_serial;
599    UNSERIALIZE_SCALAR(clcdCrsrMis_serial);
600    clcdCrsrMis = clcdCrsrMis_serial;
601
602    UNSERIALIZE_SCALAR(clock);
603    UNSERIALIZE_SCALAR(height);
604    UNSERIALIZE_SCALAR(width);
605
606    UNSERIALIZE_ARRAY(dmaBuffer, height*width);
607    UNSERIALIZE_ARRAY(frameBuffer, height*width);
608    UNSERIALIZE_SCALAR(startTime);
609    UNSERIALIZE_SCALAR(startAddr);
610    UNSERIALIZE_SCALAR(maxAddr);
611    UNSERIALIZE_SCALAR(curAddr);
612    UNSERIALIZE_SCALAR(waterMark);
613    UNSERIALIZE_SCALAR(dmaPendingNum);
614}
615
616void
617Pl111::generateInterrupt()
618{
619    DPRINTF(PL111, "Generate Interrupt: lcdImsc=0x%x lcdRis=0x%x lcdMis=0x%x\n",
620            lcdImsc, lcdRis, lcdMis);
621    lcdMis = lcdImsc & lcdRis;
622
623    if (lcdMis.ffufie || lcdMis.nbupie || lcdMis.vtcpie || lcdMis.ahmeie) {
624        gic->sendInt(intNum);
625        DPRINTF(PL111, " -- Generated\n");
626    }
627}
628
629void
630Pl111::addressRanges(AddrRangeList& range_list)
631{
632    range_list.clear();
633    range_list.push_back(RangeSize(pioAddr, pioSize));
634}
635
636Pl111 *
637Pl111Params::create()
638{
639    return new Pl111(this);
640}
641
642// bitmap class ctor
643Bitmap::Bitmap(std::fstream& bmp, uint16_t h, uint16_t w)
644{
645    Magic  magic  = {{'B','M'}};
646    Header header = {sizeof(Color)*w*h , 0, 0, 54};
647    Info   info   = {sizeof(Info), w, h, 1, sizeof(Color)*8, 0,
648                     ( sizeof(Color) *(w*h) ), 1, 1, 0, 0};
649
650    bmp.write(reinterpret_cast<char*>(&magic),  sizeof(magic));
651    bmp.write(reinterpret_cast<char*>(&header), sizeof(header));
652    bmp.write(reinterpret_cast<char*>(&info),   sizeof(info));
653}
654