Terminal program for MailStation devices
at master 800 lines 16 kB view raw
1; vim:syntax=z8a:ts=8 2; 3; msTERM 4; putchar 5; 6; Copyright (c) 2019 joshua stein <jcs@jcs.org> 7; 8; Permission to use, copy, modify, and distribute this software for any 9; purpose with or without fee is hereby granted, provided that the above 10; copyright notice and this permission notice appear in all copies. 11; 12; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19; 20 21 .module putchar 22 23 .include "mailstation.inc" 24 25 ; screen contents (characters) array in upper memory 26 .equ _screenbuf, #0xc000 27 .equ _screenbufend, #0xc3ff 28 29 ; per-character attributes array in upper memory 30 .equ _screenattrs, #0xc400 31 .equ _screenattrsend, #0xc7ff 32 33 .area _DATA 34 35font_data:: 36 .include "font/spleen-5x8.inc" 37 38 ; lookup table for putchar 39 ; left-most 5 bits are col group for lcd_cas 40 ; last 3 bits are offset into col group 41cursorx_lookup_data:: 42 .include "cursorx_lookup.inc" 43 44_cursorx:: ; cursor x position, 0-indexed 45 .db #0 46_cursory:: ; cursor y position, 0-indexed 47 .db #0 48_saved_cursorx:: ; cursor x position, 0-indexed 49 .db #0 50_saved_cursory:: ; cursor y position, 0-indexed 51 .db #0 52_putchar_sgr:: ; current SGR for putchar() 53 .db #0 54 55 56 .area _CODE 57 58; void lcd_cas(unsigned char col) 59; enable CAS, address the LCD column col (in h), and disable CAS 60_lcd_cas:: 61 push ix 62 ld ix, #0 63 add ix, sp 64 push de 65 push hl 66 ld hl, (p2shadow) 67 ld a, (hl) 68 and #0b11110111 ; CAS(0) - turn port2 bit 3 off 69 ld (hl), a 70 out (#0x02), a ; write p2shadow to port2 71 ld de, #LCD_START 72 ld a, 4(ix) 73 ld (de), a ; write col argument 74 ld a, (hl) 75 or #0b00001000 ; CAS(1) - turn port2 bit 3 on 76 ld (hl), a 77 out (#0x02), a 78 pop hl 79 pop de 80 ld sp, ix 81 pop ix 82 ret 83 84 85; void clear_screen(void) 86_clear_screen:: 87 di 88 push hl 89 in a, (#SLOT_DEVICE) 90 ld h, a 91 in a, (#SLOT_PAGE) 92 ld l, a 93 push hl 94 ld a, #DEVICE_LCD_RIGHT 95 out (#SLOT_DEVICE), a 96 call _clear_lcd_half 97 ld a, #DEVICE_LCD_LEFT 98 out (#SLOT_DEVICE), a 99 call _clear_lcd_half 100 pop hl 101 ld a, h 102 out (#SLOT_DEVICE), a 103 ld a, l 104 out (#SLOT_PAGE), a 105 pop hl 106 ei 107 ret 108 109_clear_screen_bufs:: 110 di 111 push bc 112 push de 113 push hl 114 xor a 115 ld (_cursorx), a 116 ld (_cursory), a 117 ld (_saved_cursorx), a 118 ld (_saved_cursory), a 119 ld (_putchar_sgr), a 120zero_screenbuf: 121 ld hl, #_screenbuf 122 ld de, #_screenbuf + 1 123 ld bc, #_screenbufend - _screenbuf 124 ld (hl), #' ' 125 ldir 126zero_screenattrs: 127 ld hl, #_screenattrs 128 ld de, #_screenattrs + 1 129 ld bc, #_screenattrsend - _screenattrs 130 ld (hl), #0 131 ldir 132clear_screen_out: 133 pop hl 134 pop de 135 pop bc 136 ei 137 ret 138 139 140; void clear_lcd_half(void) 141; zero out the current LCD module (must already be in SLOT_DEVICE) 142; from v2.54 firmware at 0x2490 143_clear_lcd_half:: 144 push bc 145 push de 146 ld b, #20 ; do 20 columns total 147clear_lcd_column: 148 ld h, #0 149 ld a, b 150 dec a ; columns are 0-based 151 ld l, a 152 push hl 153 call _lcd_cas 154 pop hl 155 push bc ; preserve our column counter 156 ld hl, #LCD_START 157 ld (hl), #0 ; zero out hl, then copy it to de 158 ld de, #LCD_START + 1 ; de will always be the next line 159 ld bc, #128 - 1 ; iterate (LCD_HEIGHT - 1) times 160 ldir ; ld (de), (hl), bc-- until 0 161 pop bc ; restore column counter 162 djnz clear_lcd_column ; column--, if not zero keep going 163clear_done: 164 pop de 165 pop bc 166 ret 167 168 169; void redraw_screen(void) 170_redraw_screen:: 171 push bc 172 push de 173 push hl 174 ld b, #0 175redraw_rows: 176 ld d, b ; store rows in d 177 ld b, #0 178redraw_cols: 179 push bc ; XXX figure out what is corrupting 180 push de ; bc and de in stamp_char, these shouldn't be needed 181 push hl 182 ld h, #0 ; cols 183 ld l, b 184 push hl 185 ld h, #0 ; rows 186 ld l, d 187 push hl 188 call _stamp_char 189 pop hl 190 pop hl 191 pop hl 192 pop de 193 pop bc 194redraw_cols_next: 195 inc hl 196 inc b 197 ld a, b 198 cp #LCD_COLS 199 jr nz, redraw_cols 200 ld b, d 201 inc b 202 ld a, b 203 cp #LCD_ROWS 204 jr nz, redraw_rows 205redraw_screen_out: 206 pop hl 207 pop de 208 pop bc 209 ret 210 211 212; void scroll_lcd(void) 213; scroll entire screen up by FONT_HEIGHT rows, minus statusbar 214_scroll_lcd:: 215 di 216 push bc 217 push de 218 push hl 219 in a, (#SLOT_DEVICE) 220 ld h, a 221 in a, (#SLOT_PAGE) 222 ld l, a 223 push hl 224 ld a, #DEVICE_LCD_LEFT 225 out (#SLOT_DEVICE), a 226 call _scroll_lcd_half 227 ld a, #DEVICE_LCD_RIGHT 228 out (#SLOT_DEVICE), a 229 call _scroll_lcd_half 230 pop hl 231 ld a, h 232 out (#SLOT_DEVICE), a 233 ld a, l 234 out (#SLOT_PAGE), a 235shift_bufs: 236 ld b, #0 237screenbuf_shift_loop: 238 ld h, b 239 ld l, #0 240 call screenbuf_offset 241 ld de, #_screenbuf 242 add hl, de ; hl = screenbuf[b * LCD_COLS] 243 push hl 244 ld de, #LCD_COLS 245 add hl, de ; hl += LCD_COLS 246 pop de ; de = screenbuf[b * LCD_COLS] 247 push bc 248 ld bc, #LCD_COLS 249 ldir ; ld (de), (hl), de++, hl++, bc-- 250 pop bc 251 inc b 252 ld a, b 253 cp #TEXT_ROWS - 1 254 jr nz, screenbuf_shift_loop 255screenattrs_shift: 256 ld b, #0 257screenattrs_shift_loop: 258 ld h, b 259 ld l, #0 260 call screenbuf_offset 261 ld de, #_screenattrs 262 add hl, de ; hl = screenattrs[b * LCD_COLS] 263 push hl 264 ld de, #LCD_COLS 265 add hl, de 266 pop de 267 push bc 268 ld bc, #LCD_COLS 269 ldir 270 pop bc 271 inc b 272 ld a, b 273 cp #TEXT_ROWS - 1 274 jr nz, screenattrs_shift_loop 275last_row_zero: 276 ld a, #TEXT_ROWS - 1 277 ld h, a 278 ld l, #0 279 call screenbuf_offset 280 ld de, #_screenbuf 281 add hl, de 282 ld d, #0 283 ld e, #LCD_COLS - 1 284 add hl, de 285 ld b, #LCD_COLS 286 ld a, (_putchar_sgr) 287last_row_zero_loop: 288 ld (hl), #' ' 289 dec hl 290 djnz last_row_zero_loop 291scroll_lcd_out: 292 pop hl 293 pop de 294 pop bc 295 ei 296 ret 297 298 299; void scroll_lcd_half(void) 300; scroll current LCD module up by FONT_HEIGHT rows, minus statusbar and 301; zero out the last line of text (only to the LCD) 302_scroll_lcd_half:: 303 push ix 304 ld ix, #0 305 add ix, sp 306 push bc 307 push de 308 push hl 309 ; alloc 2 bytes on the stack for local storage 310 push hl 311 ld a, #LCD_HEIGHT - (FONT_HEIGHT * 2) ; iterations of pixel row moves 312scroll_init: 313 ld -1(ix), a ; store iterations 314 ld b, #20 ; do 20 columns total 315scroll_lcd_column: 316 ld -2(ix), b ; store new column counter 317 ld a, b 318 sub #1 ; columns are 0-based 319 ld h, #0 320 ld l, a 321 push hl 322 call _lcd_cas 323 pop hl 324scroll_rows: 325 ld b, #0 326 ld c, -1(ix) ; bc = row counter 327 ld hl, #LCD_START + 8 ; start of next line 328 ld de, #LCD_START 329 ldir ; ld (de), (hl), bc-- until 0 330scroll_zerolast: 331 ld hl, #LCD_START 332 ld d, #0 333 ld e, -1(ix) 334 add hl, de 335 ld b, #FONT_HEIGHT 336scroll_zerolastloop: ; 8 times: zero hl, hl++ 337 ld (hl), #0 338 inc hl 339 djnz scroll_zerolastloop 340 ld b, -2(ix) 341 djnz scroll_lcd_column ; column--, if not zero keep going 342 pop hl 343 pop de 344 pop bc 345 ld sp, ix 346 pop ix 347 ret 348 349 350; address of screenbuf or screenattrs offset for a row/col in hl, returns in hl 351screenbuf_offset: 352 push bc 353 push de 354 ; uses hl 355 ex de, hl 356 ld hl, #0 357 ld a, d ; row 358 cp #0 359 jr z, multiply_srow_out ; only add rows if > 0 360 ld bc, #LCD_COLS 361multiply_srow: 362 add hl, bc 363 dec a 364 cp #0 365 jr nz, multiply_srow 366multiply_srow_out: 367 ld d, #0 ; col in e 368 add hl, de ; hl = (row * LCD_COLS) + col 369 pop de 370 pop bc 371 ret ; hl 372 373 374; void stamp_char(unsigned int row, unsigned int col) 375; row at 4(ix), col at 6(ix) 376_stamp_char:: 377 push ix 378 ld ix, #0 379 add ix, sp 380 push bc 381 push de 382 push hl 383 ld hl, #-15 ; stack bytes for local storage 384 add hl, sp 385 ld sp, hl 386 in a, (#SLOT_DEVICE) 387 ld -3(ix), a ; stack[-3] = old slot device 388 in a, (#SLOT_PAGE) 389 ld -4(ix), a ; stack[-4] = old slot page 390find_char: 391 ld h, 4(ix) 392 ld l, 6(ix) 393 call screenbuf_offset 394 push hl 395 ld de, #_screenbuf 396 add hl, de ; hl = screenbuf[(row * LCD_COLS) + col] 397 ld a, (hl) 398 ld -5(ix), a ; stack[-5] = character to stamp 399 pop hl 400 ld de, #_screenattrs 401 add hl, de ; hl = screenattrs[(row * LCD_COLS) + col] 402 ld a, (hl) 403 ld -6(ix), a ; stack[-6] = character attrs 404calc_font_data_base: 405 ld h, #0 406 ld l, -5(ix) ; char 407 add hl, hl ; hl = char * FONT_HEIGHT (8) 408 add hl, hl 409 add hl, hl 410 ld de, #font_data 411 add hl, de 412 ld -7(ix), l 413 ld -8(ix), h ; stack[-8,-7] = char font data base addr 414calc_char_cell_base: 415 ld h, #0 416 ld l, 4(ix) ; row 417 add hl, hl 418 add hl, hl 419 add hl, hl ; hl = row * FONT_HEIGHT (8) 420 ld de, #LCD_START 421 add hl, de ; hl = 4038 + (row * FONT_HEIGHT) 422 ld -9(ix), l 423 ld -10(ix), h ; stack[-10,-9] = lcd char cell base 424fetch_from_table: 425 ld a, 6(ix) ; col 426 ld hl, #cursorx_lookup_data 427 ld b, #0 428 ld c, a 429 add hl, bc 430 ld b, (hl) 431 ld a, b 432pluck_col_group: 433 and #0b11111000 ; upper 5 bits are col group 434 srl a 435 srl a 436 srl a 437 ld -11(ix), a ; stack[-11] = col group 438pluck_offset: 439 ld a, b 440 and #0b00000111 ; lower 3 bits are offset 441 ld -12(ix), a ; stack[-12] = offset 442 ld -15(ix), #0 ; stack[-15] = previous lcd col 443 ld d, #FONT_HEIGHT ; for (row = FONT_HEIGHT; row >= 0; row--) 444next_char_row: 445 ld a, d 446 dec a 447 ld h, -8(ix) ; char font data base 448 ld l, -7(ix) 449 ld b, #0 450 ld c, a 451 add hl, bc 452 ld a, (hl) ; font_addr + (char * FONT_HEIGHT) + row 453 ld b, -6(ix) 454 bit #ATTR_BIT_REVERSE, b 455 jr nz, reverse 456 bit #ATTR_BIT_CURSOR, b 457 jr nz, reverse 458 jr not_reverse 459reverse: 460 cpl ; flip em 461 and #0b00011111 ; mask off bits not within FONT_WIDTH 462not_reverse: 463 ld -13(ix), a ; stack[-13] = working font data 464 ld a, -6(ix) 465 bit #ATTR_BIT_UNDERLINE, a 466 jr z, not_underline 467 ld a, d 468 cp #FONT_HEIGHT 469 jr nz, not_underline 470underline: 471 ld -13(ix), #0xff 472not_underline: 473 ld a, 6(ix) ; col 474 cp #LCD_COLS / 2 ; assume a char never spans both LCD sides 475 jr nc, rightside 476leftside: 477 ld a, #DEVICE_LCD_LEFT 478 jr swap_lcd 479rightside: 480 ld a, #DEVICE_LCD_RIGHT 481swap_lcd: 482 out (#SLOT_DEVICE), a 483 ld e, #FONT_WIDTH ; for (col = FONT_WIDTH; col > 0; col--) 484next_char_col: ; inner loop, each col of each row 485 ld -14(ix), #0b00011111 ; font data mask that will get shifted 486determine_cas: 487 ld c, #0 488 ld b, -11(ix) ; col group 489 ld a, -12(ix) ; bit offset 490 add #FONT_WIDTH 491 sub e ; if offset+(5-col) is >= 8, advance col 492 cp #LCD_COL_GROUP_WIDTH 493 jr c, skip_advance ; if a >= 8, advance (dec b) 494 dec b 495 ld c, -12(ix) ; bit offset 496 ld a, #LCD_COL_GROUP_WIDTH 497 sub c 498 ld c, a ; c = number of right shifts 499skip_advance: 500do_lcd_cas: 501 ld a, -15(ix) ; previous lcd cas 502 cp b 503 jr z, prep_right_shift 504 ld h, #0 505 ld l, b 506 push hl 507 call _lcd_cas 508 pop hl 509 ld -15(ix), b ; store lcd col for next round 510 ; if this character doesn't fit entirely in one lcd column, we need to 511 ; span two of them and on the left one, shift font data and masks right 512 ; to remove right-most bits that will be on the next column 513prep_right_shift: 514 ld a, c 515 cp #0 516 jr z, prep_left_shift 517 ld b, c 518 ld c, -14(ix) ; matching mask 00011111 519 ld a, -13(ix) ; load font data like 00010101 520right_shift: 521 srl a ; shift font data right #b times 522 srl c ; and mask to match 523 djnz right_shift ; -> 10101000 524 ld -14(ix), c 525 jr done_left_shift 526prep_left_shift: 527 ld c, -14(ix) ; mask 528 ld a, -12(ix) ; (bit offset) times, shift font data 529 cp #0 530 ld b, a 531 ld a, -13(ix) ; read new font data 532 jr z, done_left_shift 533left_shift: 534 sla a 535 sla c 536 djnz left_shift 537done_left_shift: 538 ld b, a 539 ld a, c 540 cpl 541 ld -14(ix), a ; store inverted mask 542 ld a, b 543read_lcd_data: 544 ld h, -10(ix) 545 ld l, -9(ix) 546 ld b, a 547 ld a, d 548 dec a 549 ld c, a 550 ld a, b 551 ld b, #0 552 add hl, bc ; hl = 4038 + (row * FONT_HEIGHT) + row - 1 553 ld b, a ; store new font data 554 ld a, (hl) ; read existing cell data 555 and -14(ix) ; mask off new char cell 556 or b ; combine data into cell 557 ld (hl), a 558 dec e 559 jp nz, next_char_col 560 dec d 561 jp nz, next_char_row 562stamp_char_out: 563 ld a, -3(ix) ; restore old slot device 564 out (#SLOT_DEVICE), a 565 ld a, -4(ix) ; restore old slot page 566 out (#SLOT_PAGE), a 567 ld hl, #15 ; remove stack bytes 568 add hl, sp 569 ld sp, hl 570 pop hl 571 pop de 572 pop bc 573 ld sp, ix 574 pop ix 575 ret 576 577 578; void uncursor(void) 579; remove cursor attribute from old cursor position 580_uncursor:: 581 push de 582 push hl 583 ld a, (_cursory) 584 ld h, a 585 ld a, (_cursorx) 586 ld l, a 587 call screenbuf_offset 588 ld de, #_screenattrs 589 add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx] 590 ld a, (hl) 591 res #ATTR_BIT_CURSOR, a ; &= ~(ATTR_CURSOR) 592 ld (hl), a 593 ld a, (_cursorx) 594 ld l, a 595 push hl 596 ld a, (_cursory) 597 ld l, a 598 push hl 599 call _stamp_char 600 pop hl 601 pop hl 602 pop hl 603 pop de 604 ret 605 606; void recursor(void) 607; force-set cursor attribute 608_recursor:: 609 push de 610 push hl 611 ld a, (_cursory) 612 ld h, a 613 ld a, (_cursorx) 614 ld l, a 615 call screenbuf_offset 616 ld de, #_screenattrs 617 add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx] 618 ld a, (hl) 619 set #ATTR_BIT_CURSOR, a 620 ld (hl), a 621 pop hl 622 pop de 623 ret 624 625 626; int putchar(int c) 627_putchar:: 628 push ix 629 ld ix, #0 630 add ix, sp ; char to print is at 4(ix) 631 push de 632 push hl 633 call _uncursor 634 ld a, 4(ix) 635 cp #'\b' ; backspace 636 jr nz, not_backspace 637backspace: 638 ld a, (_cursorx) 639 cp #0 640 jr nz, cursorx_not_zero 641 ld a, (_cursory) 642 cp #0 643 jp z, putchar_fastout ; cursorx/y at 0,0, nothing to do 644 dec a 645 ld (_cursory), a ; cursory-- 646 ld a, #LCD_COLS - 2 647 ld (_cursorx), a 648 jp putchar_draw_cursor 649cursorx_not_zero: 650 dec a 651 ld (_cursorx), a ; cursorx--; 652 jp putchar_draw_cursor 653not_backspace: 654 cp #'\r' 655 jr nz, not_cr 656 xor a 657 ld (_cursorx), a ; cursorx = 0 658 jr not_crlf 659not_cr: 660 cp #'\n' 661 jr nz, not_crlf 662 xor a 663 ld (_cursorx), a ; cursorx = 0 664 ld a, (_cursory) 665 inc a 666 ld (_cursory), a ; cursory++ 667not_crlf: 668 ld a, (_cursorx) 669 cp #LCD_COLS 670 jr c, not_longer_text_cols ; cursorx < TEXT_COLS 671 xor a 672 ld (_cursorx), a ; cursorx = 0 673 ld a, (_cursory) 674 inc a 675 ld (_cursory), a 676not_longer_text_cols: 677 ld a, (_cursory) 678 cp #TEXT_ROWS 679 jr c, scroll_out 680scroll_up_screen: 681 call _scroll_lcd 682 xor a 683 ld (_cursorx), a 684 ld a, #TEXT_ROWS - 1 685 ld (_cursory), a ; cursory = TEXT_ROWS - 1 686scroll_out: 687 ld a, 4(ix) 688 cp a, #'\r' 689 jr z, cr_or_lf 690 cp a, #'\n' 691 jr z, cr_or_lf 692 jr store_char_in_buf 693cr_or_lf: 694 jp putchar_draw_cursor 695store_char_in_buf: 696 ld a, (_cursory) 697 ld h, a 698 ld a, (_cursorx) 699 ld l, a 700 call screenbuf_offset 701 push hl 702 ld de, #_screenbuf 703 add hl, de ; hl = screenbuf[(cursory * LCD_COLS) + cursorx] 704 ld a, 4(ix) 705 ld (hl), a ; store character 706 pop hl 707 ld de, #_screenattrs 708 add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx] 709 ld a, (_putchar_sgr) 710 ld (hl), a ; = putchar_sgr 711 ld a, (_cursorx) 712 ld l, a 713 push hl 714 ld a, (_cursory) 715 ld l, a 716 push hl 717 call _stamp_char 718 pop hl 719 pop hl 720advance_cursorx: 721 ld a, (_cursorx) 722 inc a 723 ld (_cursorx), a 724 cp #LCD_COLS ; if (cursorx >= LCD_COLS) 725 jr c, putchar_draw_cursor 726 xor a 727 ld (_cursorx), a 728 ld a, (_cursory) 729 inc a 730 ld (_cursory), a 731check_cursory: 732 cp #TEXT_ROWS ; and if (cursory >= TEXT_ROWS) 733 jr c, putchar_draw_cursor 734 call _scroll_lcd 735 ld a, #TEXT_ROWS - 1 736 ld (_cursory), a ; cursory = TEXT_ROWS - 1 737putchar_draw_cursor: 738 ld a, (_cursory) 739 ld h, a 740 ld a, (_cursorx) 741 ld l, a 742 call screenbuf_offset 743 ld de, #_screenattrs 744 add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx] 745 ld a, (hl) ; read existing attrs 746 set #ATTR_BIT_CURSOR, a 747 ld (hl), a ; = putchar_sgr | ATTR_CURSOR 748 ld a, (_cursorx) 749 ld l, a 750 push hl 751 ld a, (_cursory) 752 ld l, a 753 push hl 754 call _stamp_char 755 pop hl 756 pop hl 757putchar_fastout: 758 pop hl 759 pop de 760 ld sp, ix 761 pop ix 762 ret 763 764 765; void putchar_attr(unsigned char row, unsigned char col, char c, char attr) 766; directly manipulates screenbuf/attrs without scrolling or length checks 767; row at 4(ix), col at 5(ix), c at 6(ix), attr at 7(ix) 768_putchar_attr:: 769 push ix 770 ld ix, #0 771 add ix, sp 772 push de 773 push hl 774store_char: 775 ld h, 4(ix) 776 ld l, 5(ix) 777 call screenbuf_offset 778 push hl 779 ld de, #_screenbuf 780 add hl, de ; screenbuf[(row * TEXT_COLS) + col] 781 ld a, 6(ix) 782 ld (hl), a 783store_attrs: 784 pop hl 785 ld de, #_screenattrs 786 add hl, de ; screenattrs[(row * TEXT_COLS) + col] 787 ld a, 7(ix) 788 ld (hl), a 789 ld l, 5(ix) 790 push hl 791 ld l, 4(ix) 792 push hl 793 call _stamp_char 794 pop hl 795 pop hl 796 pop hl 797 pop de 798 ld sp, ix 799 pop ix 800 ret