screen.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "io.h"
  15. #include "ureg.h"
  16. #include "../port/error.h"
  17. #define Image IMAGE
  18. #include <draw.h>
  19. #include <memdraw.h>
  20. #include <cursor.h>
  21. #include "screen.h"
  22. #define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
  23. Point ZP = {0, 0};
  24. Rectangle physgscreenr;
  25. Memdata gscreendata;
  26. Memimage *gscreen;
  27. VGAscr vgascreen[1];
  28. Cursor arrow = {
  29. { -1, -1 },
  30. { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
  31. 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
  32. 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
  33. 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
  34. },
  35. { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
  36. 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
  37. 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
  38. 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
  39. },
  40. };
  41. int didswcursorinit;
  42. static void *softscreen;
  43. int
  44. screensize(int x, int y, int z, uint32_t chan)
  45. {
  46. Proc *up = externup();
  47. VGAscr *scr;
  48. void *oldsoft;
  49. lock(&vgascreenlock);
  50. if(waserror()){
  51. unlock(&vgascreenlock);
  52. nexterror();
  53. }
  54. memimageinit();
  55. scr = &vgascreen[0];
  56. oldsoft = softscreen;
  57. if(scr->paddr == 0){
  58. int width = (x*z)/BI2WD;
  59. void *p;
  60. p = malloc(width*BY2WD*y);
  61. if(p == nil)
  62. error("no memory for vga soft screen");
  63. gscreendata.bdata = softscreen = p;
  64. if(scr->dev && scr->dev->page){
  65. scr->vaddr = KADDR(VGAMEM());
  66. scr->apsize = 1<<16;
  67. }
  68. scr->useflush = 1;
  69. }
  70. else{
  71. gscreendata.bdata = scr->vaddr;
  72. scr->useflush = scr->dev && scr->dev->flush;
  73. }
  74. scr->gscreen = nil;
  75. if(gscreen)
  76. freememimage(gscreen);
  77. gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
  78. if(gscreen == nil)
  79. error("no memory for vga memimage");
  80. vgaimageinit(chan);
  81. scr->palettedepth = 6; /* default */
  82. scr->gscreendata = &gscreendata;
  83. scr->memdefont = getmemdefont();
  84. scr->gscreen = gscreen;
  85. physgscreenr = gscreen->r;
  86. unlock(&vgascreenlock);
  87. poperror();
  88. if(oldsoft)
  89. free(oldsoft);
  90. memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
  91. flushmemscreen(gscreen->r);
  92. if(didswcursorinit)
  93. swcursorinit();
  94. drawcmap();
  95. return 0;
  96. }
  97. int
  98. screenaperture(int size, int align)
  99. {
  100. VGAscr *scr;
  101. scr = &vgascreen[0];
  102. if(scr->paddr) /* set up during enable */
  103. return 0;
  104. if(size == 0)
  105. return 0;
  106. if(scr->dev && scr->dev->linear){
  107. scr->dev->linear(scr, size, align);
  108. return 0;
  109. }
  110. /*
  111. * Need to allocate some physical address space.
  112. * The driver will tell the card to use it.
  113. */
  114. size = ROUNDUP(sizeof(size), 4*KiB);
  115. scr->paddr = (uint64_t)malloc(size);
  116. if(scr->paddr == 0)
  117. return -1;
  118. scr->vaddr = vmap(scr->paddr, size);
  119. if(scr->vaddr == nil)
  120. return -1;
  121. scr->apsize = size;
  122. return 0;
  123. }
  124. unsigned char*
  125. attachscreen(Rectangle* r, uint32_t* chan, int* d, int* width, int *softscreen)
  126. {
  127. VGAscr *scr;
  128. scr = &vgascreen[0];
  129. if(scr->gscreen == nil || scr->gscreendata == nil)
  130. return nil;
  131. *r = scr->gscreen->clipr;
  132. *chan = scr->gscreen->chan;
  133. *d = scr->gscreen->depth;
  134. *width = scr->gscreen->width;
  135. *softscreen = scr->useflush;
  136. return scr->gscreendata->bdata;
  137. }
  138. /*
  139. * It would be fair to say that this doesn't work for >8-bit screens.
  140. */
  141. void
  142. flushmemscreen(Rectangle r)
  143. {
  144. VGAscr *scr;
  145. unsigned char *sp, *disp, *sdisp, *edisp;
  146. int y, len, incs, off, page;
  147. scr = &vgascreen[0];
  148. if(scr->dev && scr->dev->flush){
  149. scr->dev->flush(scr, r);
  150. return;
  151. }
  152. if(scr->gscreen == nil || scr->useflush == 0)
  153. return;
  154. if(scr->dev == nil || scr->dev->page == nil)
  155. return;
  156. if(rectclip(&r, scr->gscreen->r) == 0)
  157. return;
  158. incs = scr->gscreen->width * BY2WD;
  159. switch(scr->gscreen->depth){
  160. default:
  161. len = 0;
  162. panic("flushmemscreen: depth\n");
  163. break;
  164. case 8:
  165. len = Dx(r);
  166. break;
  167. }
  168. if(len < 1)
  169. return;
  170. off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
  171. page = off/scr->apsize;
  172. off %= scr->apsize;
  173. disp = scr->vaddr;
  174. sdisp = disp+off;
  175. edisp = disp+scr->apsize;
  176. off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
  177. sp = scr->gscreendata->bdata + off;
  178. scr->dev->page(scr, page);
  179. for(y = r.min.y; y < r.max.y; y++) {
  180. if(sdisp + incs < edisp) {
  181. memmove(sdisp, sp, len);
  182. sp += incs;
  183. sdisp += incs;
  184. }
  185. else {
  186. off = edisp - sdisp;
  187. page++;
  188. if(off <= len){
  189. if(off > 0)
  190. memmove(sdisp, sp, off);
  191. scr->dev->page(scr, page);
  192. if(len - off > 0)
  193. memmove(disp, sp+off, len - off);
  194. }
  195. else {
  196. memmove(sdisp, sp, len);
  197. scr->dev->page(scr, page);
  198. }
  199. sp += incs;
  200. sdisp += incs - scr->apsize;
  201. }
  202. }
  203. }
  204. void
  205. getcolor(uint32_t p, uint32_t* pr, uint32_t* pg, uint32_t* pb)
  206. {
  207. VGAscr *scr;
  208. uint32_t x;
  209. scr = &vgascreen[0];
  210. if(scr->gscreen == nil)
  211. return;
  212. switch(scr->gscreen->depth){
  213. default:
  214. x = 0x0F;
  215. break;
  216. case 8:
  217. x = 0xFF;
  218. break;
  219. }
  220. p &= x;
  221. lock(&cursor.l);
  222. *pr = scr->colormap[p][0];
  223. *pg = scr->colormap[p][1];
  224. *pb = scr->colormap[p][2];
  225. unlock(&cursor.l);
  226. }
  227. int
  228. setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
  229. {
  230. VGAscr *scr;
  231. int d;
  232. scr = &vgascreen[0];
  233. d = scr->palettedepth;
  234. lock(&cursor.l);
  235. scr->colormap[p][0] = r;
  236. scr->colormap[p][1] = g;
  237. scr->colormap[p][2] = b;
  238. vgao(PaddrW, p);
  239. vgao(Pdata, r>>(32-d));
  240. vgao(Pdata, g>>(32-d));
  241. vgao(Pdata, b>>(32-d));
  242. unlock(&cursor.l);
  243. return ~0;
  244. }
  245. /*
  246. * On some video cards (e.g. Mach64), the palette is used as the
  247. * DAC registers for >8-bit modes. We don't want to set them when the user
  248. * is trying to set a colormap and the card is in one of these modes.
  249. */
  250. int
  251. setcolor(uint32_t p, uint32_t r, uint32_t g, uint32_t b)
  252. {
  253. VGAscr *scr;
  254. int x;
  255. scr = &vgascreen[0];
  256. if(scr->gscreen == nil)
  257. return 0;
  258. switch(scr->gscreen->depth){
  259. case 1:
  260. case 2:
  261. case 4:
  262. x = 0x0F;
  263. break;
  264. case 8:
  265. x = 0xFF;
  266. break;
  267. default:
  268. return 0;
  269. }
  270. p &= x;
  271. return setpalette(p, r, g, b);
  272. }
  273. int
  274. cursoron(int dolock)
  275. {
  276. VGAscr *scr;
  277. int v;
  278. scr = &vgascreen[0];
  279. if(scr->cur == nil || scr->cur->move == nil)
  280. return 0;
  281. if(dolock)
  282. lock(&cursor.l);
  283. v = scr->cur->move(scr, mousexy());
  284. if(dolock)
  285. unlock(&cursor.l);
  286. return v;
  287. }
  288. void
  289. cursoroff(int i)
  290. {
  291. }
  292. void
  293. setcursor(Cursor* curs)
  294. {
  295. VGAscr *scr;
  296. scr = &vgascreen[0];
  297. if(scr->cur == nil || scr->cur->load == nil)
  298. return;
  299. scr->cur->load(scr, curs);
  300. }
  301. int hwaccel = 1;
  302. int hwblank = 0; /* turned on by drivers that are known good */
  303. int panning = 0;
  304. int
  305. hwdraw(Memdrawparam *par)
  306. {
  307. VGAscr *scr;
  308. Memimage *dst, *src, *mask;
  309. int m;
  310. if(hwaccel == 0)
  311. return 0;
  312. scr = &vgascreen[0];
  313. if((dst=par->dst) == nil || dst->data == nil)
  314. return 0;
  315. if((src=par->src) == nil || src->data == nil)
  316. return 0;
  317. if((mask=par->mask) == nil || mask->data == nil)
  318. return 0;
  319. if(scr->cur == &swcursor){
  320. /*
  321. * always calling swcursorhide here doesn't cure
  322. * leaving cursor tracks nor failing to refresh menus
  323. * with the latest libmemdraw/draw.c.
  324. */
  325. if(dst->data->bdata == gscreendata.bdata)
  326. swcursoravoid(par->r);
  327. if(src->data->bdata == gscreendata.bdata)
  328. swcursoravoid(par->sr);
  329. if(mask->data->bdata == gscreendata.bdata)
  330. swcursoravoid(par->mr);
  331. }
  332. if(dst->data->bdata != gscreendata.bdata)
  333. return 0;
  334. if(scr->fill==nil && scr->scroll==nil)
  335. return 0;
  336. /*
  337. * If we have an opaque mask and source is one opaque
  338. * pixel we can convert to the destination format and just
  339. * replicate with memset.
  340. */
  341. m = Simplesrc|Simplemask|Fullmask;
  342. if(scr->fill
  343. && (par->state&m)==m
  344. && ((par->srgba&0xFF) == 0xFF)
  345. && (par->op&S) == S)
  346. return scr->fill(scr, par->r, par->sdval);
  347. /*
  348. * If no source alpha, an opaque mask, we can just copy the
  349. * source onto the destination. If the channels are the same and
  350. * the source is not replicated, memmove suffices.
  351. */
  352. m = Simplemask|Fullmask;
  353. if(scr->scroll
  354. && src->data->bdata==dst->data->bdata
  355. && !(src->flags&Falpha)
  356. && (par->state&m)==m
  357. && (par->op&S) == S)
  358. return scr->scroll(scr, par->r, par->sr);
  359. return 0;
  360. }
  361. void
  362. blankscreen(int blank)
  363. {
  364. VGAscr *scr;
  365. scr = &vgascreen[0];
  366. if(hwblank){
  367. if(scr->blank)
  368. scr->blank(scr, blank);
  369. else
  370. vgablank(scr, blank);
  371. }
  372. }
  373. void
  374. vgalinearpciid(VGAscr *scr, int vid, int did)
  375. {
  376. Pcidev *p;
  377. p = nil;
  378. while((p = pcimatch(p, vid, 0)) != nil){
  379. if(p->ccrb != 3) /* video card */
  380. continue;
  381. if(did != 0 && p->did != did)
  382. continue;
  383. break;
  384. }
  385. if(p == nil)
  386. error("pci video card not found");
  387. scr->pci = p;
  388. vgalinearpci(scr);
  389. }
  390. void
  391. vgalinearpci(VGAscr *scr)
  392. {
  393. uint32_t paddr;
  394. int i, size, best;
  395. Pcidev *p;
  396. p = scr->pci;
  397. if(p == nil)
  398. return;
  399. /*
  400. * Scan for largest memory region on card.
  401. * Some S3 cards (e.g. Savage) have enormous
  402. * mmio regions (but even larger frame buffers).
  403. * Some 3dfx cards (e.g., Voodoo3) have mmio
  404. * buffers the same size as the frame buffer,
  405. * but only the frame buffer is marked as
  406. * prefetchable (bar&8). If a card doesn't fit
  407. * into these heuristics, its driver will have to
  408. * call vgalinearaddr directly.
  409. */
  410. best = -1;
  411. for(i=0; i<nelem(p->mem); i++){
  412. if(p->mem[i].bar&1) /* not memory */
  413. continue;
  414. if(p->mem[i].size < 640*480) /* not big enough */
  415. continue;
  416. if(best==-1
  417. || p->mem[i].size > p->mem[best].size
  418. || (p->mem[i].size == p->mem[best].size
  419. && (p->mem[i].bar&8)
  420. && !(p->mem[best].bar&8)))
  421. best = i;
  422. }
  423. if(best >= 0){
  424. paddr = p->mem[best].bar & ~0x0F;
  425. size = p->mem[best].size;
  426. vgalinearaddr(scr, paddr, size);
  427. return;
  428. }
  429. error("no video memory found on pci card");
  430. }
  431. void
  432. vgalinearaddr(VGAscr *scr, uint32_t paddr, int size)
  433. {
  434. int x, nsize;
  435. uint32_t npaddr;
  436. /*
  437. * new approach. instead of trying to resize this
  438. * later, let's assume that we can just allocate the
  439. * entire window to start with.
  440. */
  441. if(scr->paddr == paddr && size <= scr->apsize)
  442. return;
  443. if(scr->paddr){
  444. /*
  445. * could call vunmap and vmap,
  446. * but worried about dangling pointers in devdraw
  447. */
  448. error("cannot grow vga frame buffer");
  449. }
  450. /* round to page boundary, just in case */
  451. x = paddr&(4*KiB-1);
  452. npaddr = paddr-x;
  453. nsize = ROUNDUP(size+x, 4*KiB);
  454. /*
  455. * Don't bother trying to map more than 4000x4000x32 = 64MB.
  456. * We only have a 256MB window.
  457. */
  458. if(nsize > 64*MB)
  459. nsize = 64*MB;
  460. scr->vaddr = vmap(npaddr, nsize);
  461. if(scr->vaddr == 0)
  462. error("cannot allocate vga frame buffer");
  463. scr->vaddr = (char*)scr->vaddr+x;
  464. scr->paddr = paddr;
  465. scr->apsize = nsize;
  466. }
  467. /*
  468. * Software cursor.
  469. */
  470. int swvisible; /* is the cursor visible? */
  471. int swenabled; /* is the cursor supposed to be on the screen? */
  472. Memimage* swback; /* screen under cursor */
  473. Memimage* swimg; /* cursor image */
  474. Memimage* swmask; /* cursor mask */
  475. Memimage* swimg1;
  476. Memimage* swmask1;
  477. Point swoffset;
  478. Rectangle swrect; /* screen rectangle in swback */
  479. Point swpt; /* desired cursor location */
  480. Point swvispt; /* actual cursor location */
  481. int swvers; /* incremented each time cursor image changes */
  482. int swvisvers; /* the version on the screen */
  483. /*
  484. * called with drawlock locked for us, most of the time.
  485. * kernel prints at inopportune times might mean we don't
  486. * hold the lock, but memimagedraw is now reentrant so
  487. * that should be okay: worst case we get cursor droppings.
  488. */
  489. void
  490. swcursorhide(void)
  491. {
  492. if(swvisible == 0)
  493. return;
  494. if(swback == nil)
  495. return;
  496. swvisible = 0;
  497. memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
  498. flushmemscreen(swrect);
  499. }
  500. void
  501. swcursoravoid(Rectangle r)
  502. {
  503. if(swvisible && rectXrect(r, swrect))
  504. swcursorhide();
  505. }
  506. void
  507. swcursordraw(void)
  508. {
  509. if(swvisible)
  510. return;
  511. if(swenabled == 0)
  512. return;
  513. if(swback == nil || swimg1 == nil || swmask1 == nil)
  514. return;
  515. assert(!canqlock(&drawlock));
  516. swvispt = swpt;
  517. swvisvers = swvers;
  518. swrect = rectaddpt(Rect(0,0,16,16), swvispt);
  519. memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
  520. memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
  521. flushmemscreen(swrect);
  522. swvisible = 1;
  523. }
  524. /*
  525. * Need to lock drawlock for ourselves.
  526. */
  527. void
  528. swenable(VGAscr *v)
  529. {
  530. swenabled = 1;
  531. if(canqlock(&drawlock)){
  532. swcursordraw();
  533. qunlock(&drawlock);
  534. }
  535. }
  536. void
  537. swdisable(VGAscr *v)
  538. {
  539. swenabled = 0;
  540. if(canqlock(&drawlock)){
  541. swcursorhide();
  542. qunlock(&drawlock);
  543. }
  544. }
  545. void
  546. swload(VGAscr *v, Cursor *curs)
  547. {
  548. unsigned char *ip, *mp;
  549. int i, j, set, clr;
  550. if(!swimg || !swmask || !swimg1 || !swmask1)
  551. return;
  552. /*
  553. * Build cursor image and mask.
  554. * Image is just the usual cursor image
  555. * but mask is a transparent alpha mask.
  556. *
  557. * The 16x16x8 memimages do not have
  558. * padding at the end of their scan lines.
  559. */
  560. ip = byteaddr(swimg, ZP);
  561. mp = byteaddr(swmask, ZP);
  562. for(i=0; i<32; i++){
  563. set = curs->set[i];
  564. clr = curs->clr[i];
  565. for(j=0x80; j; j>>=1){
  566. *ip++ = set&j ? 0x00 : 0xFF;
  567. *mp++ = (clr|set)&j ? 0xFF : 0x00;
  568. }
  569. }
  570. swoffset = curs->offset;
  571. swvers++;
  572. memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
  573. memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
  574. }
  575. int
  576. swmove(VGAscr *v, Point p)
  577. {
  578. swpt = addpt(p, swoffset);
  579. return 0;
  580. }
  581. void
  582. swcursorclock(void)
  583. {
  584. int x;
  585. if(!swenabled)
  586. return;
  587. if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
  588. return;
  589. x = splhi();
  590. if(swenabled)
  591. if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
  592. if(canqlock(&drawlock)){
  593. swcursorhide();
  594. swcursordraw();
  595. qunlock(&drawlock);
  596. }
  597. splx(x);
  598. }
  599. void
  600. swcursorinit(void)
  601. {
  602. static int init, warned;
  603. VGAscr *scr;
  604. didswcursorinit = 1;
  605. scr = &vgascreen[0];
  606. if(scr==nil || scr->gscreen==nil)
  607. return;
  608. if(scr->dev == nil || scr->dev->linear == nil){
  609. if(!warned){
  610. print("cannot use software cursor on non-linear vga screen\n");
  611. warned = 1;
  612. }
  613. return;
  614. }
  615. if(swback){
  616. freememimage(swback);
  617. freememimage(swmask);
  618. freememimage(swmask1);
  619. freememimage(swimg);
  620. freememimage(swimg1);
  621. }
  622. swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
  623. swmask = allocmemimage(Rect(0,0,16,16), GREY8);
  624. swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
  625. swimg = allocmemimage(Rect(0,0,16,16), GREY8);
  626. swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
  627. if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
  628. print("software cursor: allocmemimage fails");
  629. return;
  630. }
  631. memfillcolor(swmask, DOpaque);
  632. memfillcolor(swmask1, DOpaque);
  633. memfillcolor(swimg, DBlack);
  634. memfillcolor(swimg1, DBlack);
  635. if(!init){
  636. init = 1;
  637. addclock0link(swcursorclock, 10);
  638. }
  639. }
  640. VGAcur swcursor =
  641. {
  642. "soft",
  643. swenable,
  644. swdisable,
  645. swload,
  646. swmove,
  647. };
  648. // A bit hokey but it saves dumbness in the build tool and other code.
  649. VGAcur vgavesacur =
  650. {
  651. "vesa",
  652. swenable,
  653. swdisable,
  654. swload,
  655. swmove,
  656. };