2 Skelton for retropc emulator
4 Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
8 History: 2017.01.16 Initial.
12 #include "./towns_vram.h"
14 #define CLEAR_COLOR RGBA_COLOR(0,0,0,0)
17 #define _USE_ALPHA_CHANNEL
22 void TOWNS_VRAM::initialize()
24 #ifdef _USE_ALPHA_CHANNEL
25 for(int i = 0; i < 32768; i++) {
26 uint8_t g = (i / (32 * 32)) & 0x1f;
27 uint8_t r = (i / 32) & 0x1f;
29 table_32768c[i] = RGBA_COLOR(r << 3, g << 3, b << 3, 0xff);
31 for(int i = 32768; i < 65536; i++) {
32 table_32768c[i] = _CLEAR_COLOR;
35 for(int i = 0; i < TOWNS_CRTC_MAX_LINES; i++) {
36 line_rendered[0][i] = false;
37 line_rendered[1][i] = false;
41 uint32_t TOWNS_VRAM::read_data8(uint32_t addr)
45 #ifdef __LITTLE_ENDIAN__
46 uint8_t*p = (uint8_t*)vram;
47 return (uint32_t)(p[addr & 0x7ffff]);
50 d.w= vram[(addr & 0x7ffff) >> 1];
51 if((addr & 0x01) == 0) { // Hi
52 return (uint32_t)(d.b.h);
54 return (uint32_t)(d.b.l);
61 uint32_t TOWNS_VRAM::read_data16(uint32_t addr)
65 // Host: Little endian
66 // ToDo: Mask Register.
67 uint32_t naddr = (addr & 0x7fffe) >> 1;
68 #ifdef __LITTLE_ENDIAN__
69 uint16_t w = vram[naddr];
71 #else // __BIG_ENDIAN__
74 return (uint32_t)(nw.w);
78 uint32_t TOWNS_VRAM::read_data32(uint32_t addr)
81 // ToDo: Mask Register.
82 #ifdef __LITTLE_ENDIAN__
83 uint32_t*p = (uint32_t*)vram;
84 uint32_t naddr = (addr & 0x7ffff) >> 2;
85 uint32_t w = p[naddr];
87 #else // __BIG_ENDIAN__
89 uint32_t naddr = (addr & 0x7ffff) >> 1;
91 nd.w.h = vram[naddr + 1];;
96 void TOWNS_VRAM::write_data8(uint32_t addr, uint32_t data)
100 // ToDo: Mask Register.
102 uint32_t naddr = addr & 0x7ffff;
104 #ifdef __LITTLE_ENDIAN__
105 uint8_t* p = (uint8_t*)vram;
109 // ToDo: Mask register
110 d.w = vram[naddr >> 1];
111 if((addr & 0x01) != 0) { // Hi
119 // ToDo: Mask register
121 dirty = ((uint8_t)data != n) ? true : false;
124 dirty_flag[naddr >> 3] = true;
125 // ToDo: Mask register
126 #ifdef __LITTLE_ENDIAN__
129 vram[naddr >> 1] = d.w;
133 if((naddr & 1) != 0) {
134 // Upper address: Change mask for 32768colors.
135 uint16_t nd = (n < 0x80) ? 0xffff : 0x0000;
136 alpha_32768_byte[naddr >> 1] = (uint8_t)nd;
137 alpha_32768_word[naddr >> 1] = nd;
138 mask_32768_word[naddr >> 1] = ~nd;
140 // ToDo: Mask/alpha16.
141 uint16_t nu = ((n & 0xf0) != 0) ? 0x0000 : 0xffff;
142 uint16_t nl = ((n & 0x0f) != 0) ? 0x0000 : 0xffff;
144 alpha_16_byte[naddr + 0] = (uint8_t)nu;
145 alpha_16_byte[naddr + 1] = (uint8_t)nl;
146 alpha_16_word[naddr + 0] = nu;
147 alpha_16_word[naddr + 1] = nl;
148 mask_16_byte[naddr + 0] = ~nu;
149 mask_16_byte[naddr + 1] = ~nl;
154 void TOWNS_VRAM::write_data16(uint32_t addr, uint32_t data)
159 uint32_t naddr = (addr & 0x7fffe) >> 1;
161 // ToDo: Mask register
163 dirty = ((uint16_t)data != d) ? true : false;
166 dirty_flag[naddr >> 2] = true;
167 // ToDo: Mask register
170 uint16_t alphaw = (nw.w < 0x8000) ? 0xffff : 0x0000;
171 alpha_32768_byte[naddr >> 1] = (uint8_t)alphaw;
172 alpha_32768_word[naddr >> 1] = alphaw;
173 mask_32768_word[naddr >> 1] = ~alphaw;
176 uint16_t n0 = ((nw.b.h & 0xf0) != 0) ? 0x0000 : 0xffff;
177 uint16_t n1 = ((nw.b.h & 0x0f) != 0) ? 0x0000 : 0xffff;
178 uint16_t n2 = ((nw.b.l & 0xf0) != 0) ? 0x0000 : 0xffff;
179 uint16_t n3 = ((nw.b.l & 0x0f) != 0) ? 0x0000 : 0xffff;
181 alpha_16_byte[naddr + 0] = (uint8_t)n0;
182 alpha_16_byte[naddr + 1] = (uint8_t)n1;
183 alpha_16_byte[naddr + 2] = (uint8_t)n2;
184 alpha_16_byte[naddr + 3] = (uint8_t)n3;
185 alpha_16_word[naddr + 0] = n0;
186 alpha_16_word[naddr + 1] = n1;
187 alpha_16_word[naddr + 2] = n2;
188 alpha_16_word[naddr + 3] = n3;
190 mask_16_byte[naddr + 0] = ~n0;
191 mask_16_byte[naddr + 1] = ~n1;
192 mask_16_byte[naddr + 2] = ~n2;
193 mask_16_byte[naddr + 3] = ~n3;
198 void TOWNS_VRAM::write_data32(uint32_t addr, uint32_t data)
203 uint32_t naddr = (addr & 0x7fffc) >> 1;
204 uint32_t *p = (uint32_t*)vram;
207 // ToDo: Mask register
208 #ifdef __LITTLE_ENDIAN__
209 nw.d = p[naddr >> 1];
211 nw.w.l = vram[naddr + 0];
212 nw.w.h = vram[naddr + 1];
214 // ToDo: Mask register
215 dirty = (data != nw.d) ? true : false;
218 // ToDo: Mask register
219 dirty_flag[(naddr >> 2) + 0] = true;
221 #ifdef __LITTLE_ENDIAN__
222 p[naddr >> 1] = data;
225 vram[naddr + 0] = nw.w.l;
226 vram[naddr + 1] = nw.w.h;
232 uint32_t TOWNS_VRAM::read_plane_data8(uint32_t addr)
237 uint8_t *p = (uint8_t*)vram;
240 // ToDo: Writing plane.
241 if(access_page1) x_addr = 0x40000; //?
242 addr = (addr & 0x7fff) << 3;
243 p = &(p[x_addr + addr]);
246 uint32_t *pp = (uint32_t *)p;
248 uint32_t tmp_d = *pp;
250 #ifdef __LITTLE_ENDIAN__
251 uint32_t tmp_m1 = 0x000000f0;
252 uint32_t tmp_m2 = 0x0000000f;
254 uint32_t tmp_m1 = 0xf0000000;
255 uint32_t tmp_m2 = 0x0f000000;
258 tmp_d = tmp_d & write_plane_mask;
260 for(int i = 0; i < 4; i++) {
262 tmp = tmp | (((tmp_d & tmp_m1) != 0) ? 0x02 : 0x00);
263 tmp = tmp | (((tmp_d & tmp_m2) != 0) ? 0x01 : 0x00);
265 #ifdef __LITTLE_ENDIAN__
274 uint32_t TOWNS_VRAM::read_plane_data16(uint32_t addr)
277 d.b.l = (uint8_t)(read_plane_data8(addr + 0));
278 d.b.h = (uint8_t)(read_plane_data8(addr + 1));
279 return (uint32_t)(d.w);
282 uint32_t TOWNS_VRAM::read_plane_data32(uint32_t addr)
285 d.b.l = (uint8_t)(read_plane_data8(addr + 0));
286 d.b.h = (uint8_t)(read_plane_data8(addr + 1));
287 d.b.h2 = (uint8_t)(read_plane_data8(addr + 2));
288 d.b.h3 = (uint8_t)(read_plane_data8(addr + 3));
292 void TOWNS_VRAM::write_plane_data8(uint32_t addr, uint32_t data)
297 uint8_t *p = (uint8_t*)vram;
300 // ToDo: Writing plane.
301 if(access_page1) x_addr = 0x40000; //?
302 addr = (addr & 0x7fff) << 3;
303 x_addr = x_addr + addr;
307 uint32_t *pp = (uint32_t *)p;
309 uint32_t tmp_d = data & 0xff;
310 #ifdef __LITTLE_ENDIAN__
311 uint32_t tmp_m1 = 0xf0000000 & write_plane_mask;
312 uint32_t tmp_m2 = 0x0f000000 & write_plane_mask;
314 uint32_t tmp_m1 = 0x000000f0 & write_plane_mask;
315 uint32_t tmp_m2 = 0x0000000f & write_plane_mask;
320 for(int i = 0; i < 4; i++) {
321 #ifdef __LITTLE_ENDIAN__
326 tmp = tmp | (((tmp_d & 0x02) != 0) ? tmp_m1 : 0x00);
327 tmp = tmp | (((tmp_d & 0x01) != 0) ? tmp_m2 : 0x00);
332 tmp_r1 = tmp_r1 & ~write_plane_mask;
333 tmp_r1 = tmp_d | tmp_r1;
334 if(tmp_r2 != tmp_r1) {
336 dirty_flag[x_addr >> 3] = true;
340 void TOWNS_VRAM::write_plane_data16(uint32_t addr, uint32_t data)
343 d.w = (uint16_t)data;
344 write_plane_data8(addr + 0, d.b.l);
345 write_plane_data8(addr + 1, d.b.h);
349 void TOWNS_VRAM::write_plane_data32(uint32_t addr, uint32_t data)
353 write_plane_data8(addr + 0, d.b.l);
354 write_plane_data8(addr + 1, d.b.h);
355 write_plane_data8(addr + 2, d.b.h2);
356 write_plane_data8(addr + 3, d.b.h3);
362 void TOWNS_CRTC::calc_apalette16(int layer, int index)
364 if(index < 0) return;
365 if(index > 15) return;
366 apalette_16_rgb[layer][index] =
367 ((uint16_t)(apalette_b & 0x0f)) |
368 ((uint16_t)(apalette_r & 0x0f) << 4) |
369 ((uint16_t)(apalette_g & 0x0f) << 8);
371 apalette_16_pixel[layer][index] = _CLEAR_COLOR; // ??
373 apalette_16_pixel[layer][index] = RGBA_COLOR((apalette_r & 0x0f) << 4, (apalette_g & 0x0f) << 4, (apalette_b & 0x0f) << 4, 0xff);
377 void TOWNS_CRTC::calc_apalette256(int index)
379 if(index < 0) return;
380 if(index > 255) return;
381 apalette_256_rgb[layer][index] =
382 ((uint32_t)apalette_b) |
383 ((uint32_t)apalette_r << 8) |
384 ((uint32_t)apalette_g << 16);
386 apalette_256_pixel[index] = _CLEAR_COLOR; // ??
388 apalette_256_pixel[index] = RGBA_COLOR(apalette_r, apalette_g, apalette_b, 0xff);
392 void TOWNS_CRTC::set_apalette_r(int layer, uint8_t val)
395 if(apalette_code < 16) {
396 calc_apalette16(layer, (int)apalette_code);
399 calc_apalette256((int)apalette_code % 256);
402 void TOWNS_CRTC::set_apalette_g(int layer, uint8_t val)
405 if(apalette_code < 16) {
406 calc_apalette16(layer, (int)apalette_code);
409 calc_apalette256((int)apalette_code % 256);
412 void TOWNS_CRTC::set_apalette_b(int layer, uint8_t val)
415 if(apalette_code < 16) {
416 calc_apalette16(layer, (int)apalette_code);
419 calc_apalette256((int)apalette_code % 256);
422 void TOWNS_CRTC::set_apalette_num(int layer, uint8_t val)
424 apalette_code = ((int)val) % 256;
428 void TOWNS_CRTC::render_line_16(int layer, bool upper_layer, scrntype_t *framebuffer, uint8_t *vramptr, uint32_t startaddr, uint32_t words)
430 uint32_t wordptr = 0;
431 int nwords = (int)words / 16;
435 __DECL_ALIGNED(16) uint8_t srcdat[16];
436 __DECL_ALIGNED(sizeof(scrntype_t) * 16) scrntype_t tmpdat[16];
438 __DECL_ALIGNED(sizeof(scrntype_t) * 16) scrntype_t alpha_cache_16[16];
439 __DECL_ALIGNED(sizeof(scrntype_t) * 16) scrntype_t data_cache_16[16];
441 scrntype_t *pdst = framebuffer;
442 uint8_t *pp = (uint8_t *)vramptr;
443 if(framebuffer == NULL) return;
444 if(vramptr == NULL) return;
446 uintptr_t offset_base = (uintptr_t)startaddr;
447 uintptr_t base = ((layer == 0) ? 0x00000 : 0x40000);
449 // offet_base = (offset_base + [value_of_offset_register]) & mask.
450 offset_base = (offet_base & 0x3ffff) + base;
451 pp = &(pp[offset_base]);
453 for(ip = 0; ip < nwords; ip++) {
454 if(!dirty_flag[(offset_base >> 3)]) {
460 dirty_flag[(offset_base >> 3)] = false;
461 for(int ii = 0; ii < 8; ii++) {
462 srcdat[ii << 1] = pp[ii];
463 srcdat[(ii << 1) + 1] = srcdat[ii << 1] & 0x0f;
464 srcdat[ii << 1] = (srcdat[ii << 1] & 0x0f) >> 4;
467 for(int ii = 0; ii < 16; ii++) {
468 data_cache_16[ii] = apalette_16[layer][srcdat1[ii]];
471 // ToDo: For hadrdware that does not upport alpha blending(not RGBA32).
472 for(int ii = 0; ii < 16; i++) {
473 alpha_cache_16[ii] = (srcdat[ii] == 0) ? RGBA_COLOR(0, 0, 0, 0) : RGBA_COLOR(255, 255, 255, 255);
475 for(int ii = 0; ii < 16; i++) {
476 tmpdat[ii] = alpha_cache_16_byte[ii] * data_cache_16[ii];
479 // ToDo: For hadrdware that does not upport alpha blending(not RGBA32).
480 for(int ii = 0; ii < 16; i++) {
481 alpha_cache_16[ii] = (srcdat[ii] == 0) ? RGBA_COLOR(0,0,0,255) : RGBA_COLOR(255, 255, 255, 255);
483 for(int ii = 0; ii < 16; i++) {
484 tmpdat[ii] = alpha_cache_16_byte[ii] * data_cache_16[ii];
487 for(int ii = 0; ii < 16; ii++) {
488 pdst[ii] = tmpdat[ii];
493 int mod_words = words - (nwords * 16);
494 uint8_t sdat1, sdat2;
498 offset_base = (uintptr_t)startaddr;
499 // offet_base = (offset_base + [value_of_offset_register]) & mask.
500 offset_base = ((offet_base + nwords) & 0x3ffff) + base;
501 if(dirty_flag[offset_base >> 3]) {
502 dirty_flag[offset_base >> 3] = false;
503 for(int ii = 0; ii < mod_words; i++) {
506 sdat2 = sdat1 & 0x0f;
507 sdat1 = (sdat1 & 0xf0) >> 4;
509 c1 = (sdat1 == 0) ? RGBA_COLOR(0, 0, 0, 0) : RGBA_COLOR(255, 255, 255, 255);
510 c2 = (sdat2 == 0) ? RGBA_COLOR(0, 0, 0, 0) : RGBA_COLOR(255, 255, 255, 255);
512 c1 = (sdat1 == 0) ? RGBA_COLOR(0, 0, 0, 255) : RGBA_COLOR(255, 255, 255, 255);
513 c2 = (sdat2 == 0) ? RGBA_COLOR(0, 0, 0, 255) : RGBA_COLOR(255, 255, 255, 255);
517 tmpdat[ii] = c1 * apalette_16_pixel[layer][sdat1];
519 tmpdat[ii] = c2 * apalette_16_pixel[layer][sdat2];
523 for(int ii = 0; ii < mod_words; i++) {
524 pdst[ii] = tmpdat[ii];
530 void TOWNS_CRTC::render_line_256(int layer, bool upper_layer, scrntype_t *framebuffer, uint8_t *vramptr, uint32_t startaddr, uint32_t words)
532 uint32_t wordptr = 0;
533 int nwords = (int)words / 8;
535 __DECL_ALIGNED(8) uint32_t src[2];
536 __DECL_ALIGNED(8) uint8_t srcdat[8];
537 scrntype_t *pdst = __builtin_assume_aligned(framebuffer, sizeof(scrntype_t) * 4);
538 uint32_t *pp = (uint32_t *)vramptr;
540 if(framebuffer == NULL) return;
541 if(vramptr == NULL) return;
543 uintptr_t offset_base = (uintptr_t)startaddr;
544 uintptr_t base = ((layer == 0) ? 0x00000 : 0x40000);
545 // offet_base = (offset_base + [value_of_offset_register]) & mask.
546 offset_base = (offet_base & 0x3ffff) + base;
547 pp = &(pp[offset_base >> 2]);
549 for(ip = 0; ip < nwords; ip++) {
550 if(dirty_flag[offset_base >> 3]) {
553 #if defined(__LITTLE_ENDIAN__)
554 srcdat[0] = (uint8_t)((src[0] & 0xff000000) >> 24);
555 srcdat[1] = (uint8_t)((src[0] & 0x00ff0000) >> 16);
556 srcdat[2] = (uint8_t)((src[0] & 0x0000ff00) >> 8);
557 srcdat[3] = (uint8_t) (src[0] & 0x000000ff);
559 srcdat[4] = (uint8_t)((src[1] & 0xff000000) >> 24);
560 srcdat[5] = (uint8_t)((src[1] & 0x00ff0000) >> 16);
561 srcdat[6] = (uint8_t)((src[1] & 0x0000ff00) >> 8);
562 srcdat[7] = (uint8_t) (src[1] & 0x000000ff);
564 srcdat[0] = (uint8_t)( src[0] & 0x000000ff);
565 srcdat[1] = (uint8_t)((src[0] & 0x0000ff00) >> 8);
566 srcdat[2] = (uint8_t)((src[0] & 0x00ff0000) >> 16);
567 srcdat[3] = (uint8_t)((src[0] & 0xff000000) >> 24);
569 srcdat[4] = (uint8_t)( src[1] & 0x000000ff);
570 srcdat[5] = (uint8_t)((src[1] & 0x0000ff00) >> 8);
571 srcdat[6] = (uint8_t)((src[1] & 0x00ff0000) >> 16);
572 srcdat[7] = (uint8_t)((src[1] & 0xff000000) >> 24);
574 // ToDo: Super Impose.
575 for(int i = 0; i < 8; i++) {
576 pdst[i] = apalette_256_pixel[srcdat[i]];
582 int mod_words = words - (nwords * 4);
585 uint8_t *p8 = (uint8_t *)(&pp[ip]);
586 for(int i = 0; i < mod_words; i++) {
588 // ToDo: Super Impose.
589 pdst[i] = apalette_256_pixel[src8];
594 // To be used table??
595 void TOWNS_CRTC::render_line_32768(int layer, bool do_impose, scrntype_t *pixcache, uint16_t *vramptr, uint32_t words)
597 uint32_t wordptr = 0;
598 int nwords = (int)words / 8;
601 scrntype_t *pdst = pixcache;
602 uint16_t *pp = (uint16_t *)vramptr;
603 uint16_t *cachep = (uint16_t *)srcdat;
604 __DECL_ALIGNED(16) pair16_t rgb[8];
605 __DECL_ALIGNED(8) uint8_t a[8];
606 __DECL_ALIGNED(8) uint8_t r[8];
607 __DECL_ALIGNED(8) uint8_t g[8];
608 __DECL_ALIGNED(8) uint8_t b[8];
609 __DECL_ALIGNED(sizeof(scrntype_t) * 8) scrntype_t dcache[8];
613 if(framebuffer == NULL) return;
614 if(vramptr == NULL) return;
616 uintptr_t offset_base = (uintptr_t)startaddr;
617 uintptr_t base = ((layer == 0) ? 0x00000 : 0x40000);
618 // offet_base = (offset_base + [value_of_offset_register]) & mask.
619 offset_base = (offet_base & 0x3ffff) + base;
620 pp = &(pp[offset_base >> 1]);
621 for(ip = 0; ip < nwords; ip++) {
622 if(dirty_flag[offset_base >> 3]) {
623 dirty_flags[offset_base >> 3] = false;
624 for(int i = 0; i < 8; i++) {
627 for(int i = 0; i < 8; i++) {
628 dcache[i] = _CLEAR_COLOR;
631 for(int ii = 0; ii < 8; ii++) {
633 g[ii] = rgb[ii].b.h & 0x7c;
634 b[ii] = rgb[ii].b.l & 0x1f;
636 for(int ii = 0; ii < 8; ii++) {
640 for(int ii = 0; ii < 8; ii++) {
641 r[ii] = ((rgb[ii].b.h & 0x03) << 6) | ((rgb[ii].b.l & 0xe0) >> 2);
644 for(int ii = 0; ii < 8; ii++) {
645 a[ii] = (rgb[ii].h < 0x80) ? 0xff : 0x00;
648 for(int ii = 0; ii < 8; ii++) {
652 for(int ii = 0; ii < 8; ii++) {
653 dcache[ii] = RGBA_COLOR(r[ii], g[ii], b[ii], a[ii]);
655 for(int ii = 0; ii < 8; ii++) {
656 pdst[ii] = dcache[ii];
663 for(int i = 0; i < 8; i++) {
664 dcache[i] = _CLEAR_COLOR;
666 int mod_words = words - nwords * 8;
667 for(ip = 0; ip < mod_words; ip++) {
670 g[ip] = rgb[ip].b.h & 0x7c;
671 r[ip] = ((rgb[ip].b.h & 0x03) << 6) | ((rgb[ip].b.l & 0xe0) >> 2);
672 b[ip] = (rgb[ip].b.l & 0x1f) << 3;
674 a[ip] = (rgb[ip].b.h < 0x80) ? 0xff : 0x00;
678 dcache[ip] = RGBA_COLOR(r[ip], g[ip], b[ip], a[ip]);
680 for(ip = 0; ip < mod_words; ip++) {
681 pdst[ip] = dcache[ip];
685 void TOWNS_CRTC::vzoom_pixel(scrntype_t*src, int width, int srclines, scrntype_t* dst, int stride, int dstlines, float vzoom)
687 if(src == dst) return;
688 if(vzoom <= 0.0f) return;
689 if((src == NULL) || (dst == NULL)) return;
690 if(width <= 0) return;
691 if(srclines <= 0) return;
692 if(dstlines <= 0) return;
694 scrntype_t *psrc = src;
695 scrntype_t *pdst = dst;
697 float vzoom_int = floor(vzoom);
698 float vzoom_mod = vzoom - vzoom_int;
700 int nint = (int)vzoom_int;
703 for(srcline = 0; srcline < srclines; srcline++) {
709 for(int ii = 0; ii < ifactor; ii++) {
710 // ToDo: Mask, blend.
711 memcpy(pdst, psrc, width * sizeof(scrntype_t));
713 if(linecount >= dstlines) goto _exit0;;
717 nmod = nmod + vzoom_mod;
723 void TOWNS_CRTC::hzoom_pixel(scrntype_t* src, int width, scrntype_t* dst, int dst_width, float hzoom)
725 if(src == dst) return;
726 if(hzoom <= 0.0f) return;
727 if((src == NULL) || (dst == NULL)) return;
728 if(width <= 0) return;
729 if(dst_width <= 0) return;
731 scrntype_t* pdst = dst;
732 float hzoom_int = floor(hzoom);
733 float hzoom_mod = hzoom - hzoom_int;
735 int nint = (int)hzoom_int;
736 if((nint == 1) && (hzoom_mod <= 0.0f)) {
738 scrntype_t *sp = src;
739 scrntype_t pcache[4];
743 for(int ii = 0; ii < nw; ii++) {
744 if((dx + 3) >= dst_width) break;
745 for(int j = 0; j < 4; j++) {
749 for(int j = 0; j < 4; j++) {
757 if(dx >= dst_width) return;
758 for(int ii = 0; ii < nw; ii++) {
759 if(dx >= dst_width) goto __exit_0;
762 pdst[ii] = pcache[ii];
766 scrntype_t *sp = src;
769 for(int i = 0; i < width; i++) {
770 // ToDo : Storage via SIMD.
771 if(dx >= dst_width) goto __exit_0;
778 scrntype_t pix = *sp++;
779 scrntype_t pcache[4];
782 int jfactor = ifactor / 4;
784 for(int k = 0; k < 4; k++) {
787 for(int j = 0; j < jfactor; j++) {
788 if((dx + 3) >= dst_width) break;
789 for(int k = 0; k < 4; k++) {
796 ifactor = ifactor - xcount;
797 if(ifactor < 0) ifactor = 0;
798 for(int k = 0; k < ifactor; k++) {
799 if(dx >= dst_width) goto __exit_0;
803 nmod = nmod + hzoom_mod;