;===================[ Функции распределения памяти ]===================; ; Выход: BC - FREE MEM в блоках по 16k, HL - FULL MEM ;!TEST все SLOT2 на SLOT1 ; ;----------------------------------------------------------------------; ; Определение объема памяти. ; Выход: BC - FREE MEM в блоках по 16k, HL - FULL MEM EMM.GetMemSize: IN A,(SLOT1) LD B,A LD A,SYS_PAGE OUT (SLOT1),A LD HL,SYS_PAGE.RAMD_FAT - #4000 - #4000 LD C,0 .loop: LD A,(HL) INC L JR Z,.exit AND A JR NZ,.loop INC C JR .loop .exit: LD HL,#100 ;!HARDCODE max mem pages LD A,B LD B,0 OUT (SLOT1),A RET ;----------------------------------------------------------------------; ; ;!!!!! Если используется SLOT3 со своими страницами, то не пользоваться ; стеком, там может быть SP в режиме спектрума ; ;----------------------------------------------------------------------; ; Инициализация распределения памяти. EMM.InitMem: PUSH BC PUSH HL PUSH DE IN A,(SLOT3) LD C,A LD A,SYS_PAGE OUT (SLOT3),A LD HL,SYS_PAGE.RAMD_FAT ; Адрес RAM FAT в ОЗУ. ; обнуляем таблицу XOR A .loopFree: LD (HL),A INC L JR NZ,.loopFree ; резервируем спец.страницы и страницы ZX LD B,RESERVED_PAGES.Blocks LD DE,RESERVED_PAGES ; таблица занятых системных страниц .loop: LD A,(DE) CP #FF JR Z,.exitLoop .loopBlk: INC DE LD L,A LD A,(DE) LD (HL),A CP #FF JR NZ,.loopBlk .exitLoop: INC DE DJNZ .loop LD L,A LD (HL),A ; Инициализация ключей RAM-Disks XOR A LD HL,SYS_PAGE.RAMD_KEYS; - #4000 - #4000 LD B,SYS_PAGE.RAMD_KEYS.NUM .loop2: LD (HL),A INC L DJNZ .loop2 ; [x] 04/11/2023 LD HL,SYS_PAGE.Sp_RAMD_KEYS; - #4000 - #4000 LD B,SYS_PAGE.Sp_RAMD_KEYS.NUM .loop3: LD (HL),A INC L DJNZ .loop3 ; LD A,C OUT (SLOT3),A POP DE POP HL POP BC RET ;---------------------------------------------------------------------[] RESERVED_PAGES: ; [x] Block #1 - можно освободить по ID 1 DB 1,3,4,6,7,8,9,10,11,12,13,14,15 ; for Spectrum ;!FIXIT страницы 16..31 не зарезервированы, но используются в Pentagon 512 ; ; эмулятор ПЗУ ;!FIXIT ROM-Emulator сделать динамические страницы DB #42,#43,#44,#45,#46,#47;,#48,#49,#4A,#4B ; For (BASIC128, BASIC48, TRDOS, SCORP_ROM)*2, vBIOS, vEXTENSION DB #FF ; End of the block ; ; ;; Block #2 - нельзя освобождать DB 0,2,5 ; for Spectrum and BIOS starting DB DCP_PAGE ; Ports map DB Spec_Page ; Page for Spectrum mode ; Screen pages DB #50,#51,#52,#53,#54,#55,#56,#57 DB #58,#59,#5A,#5B,#5C,#5D,#5E,#5F ; ;DB MODE_PAGE ; ????? DB CBL.BUFFER_PAGE ; Page for CBL audio DB SYS_PAGE ; Page for system (BIOS) variables DB #FF ; End of the block ; .Blocks EQU 2 ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; Получить блок памяти N bytes, ; Вход: B - число необходимых блоков ; Выход: L,A - КЛЮЧ RAM-Disk/код ошибки ; CF - признак ошибки ;EMM_FN2M: EMM.GetMem: ;PUSH DE PUSH BC IN A,(SLOT3) EX AF,AF' LD A,SYS_PAGE OUT (SLOT3),A LD C,B ; сохранить число нужных блоков LD HL,SYS_PAGE.RAMD_FAT; - #4000 - #4000 ; Цикл проверки наличия нужных блоков. .loop: DEC L JR Z,.noRAM LD A,(HL) AND A JR NZ,.loop DJNZ .loop ; Место есть ! LD B,C ; Восстановить нужный объем диска LD C,#FF ; МЕТКА КОНЦА RAM-Disk LD HL,SYS_PAGE.RAMD_FAT; - #4000 - #4000 ; Заполнить RAMD_FAT .loop2: DEC L LD A,(HL) AND A JR NZ,.loop2 LD (HL),C LD C,L DJNZ .loop2 ; L - указатель цепочки. EX AF,AF' OUT (SLOT3),A LD A,L AND A POP BC ;POP DE RET .noRAM: LD L,1 ; НЕТ ПАМЯТИ EX AF,AF' OUT (SLOT3),A LD A,L SCF POP BC ;POP DE RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; Получить блок памяти N bytes, для RAM-Disk A ; Вход: B - число необходимых блоков, A - RAM-Disk ; Выход: L,A - КЛЮЧ RAM-Disk/код ошибки ; CF - признак ошибки ;EMM.GetMem: EMM.GetMemRMD: PUSH AF CALL EMM.GetMem JR C,.error1 LD B,A POP AF CALL BLK_TO_RAMD RET NC .error2: LD L,2 ; RAM-Disk занят ;!TODO перечислить все варианты ошибок и их номера как для ДСС LD A,L RET .error1: POP HL ; чистим стек LD L,A ;SCF RET ;----------------------------------------------------------------------; ; ;----------------------------------------------------------------------; ; Освободить блок памяти ключа K ; Вход: A - КЛЮЧ RAM-Disk EMM.FreeMemRMD: CALL GET_RAMD_ST RET C SCF RET Z LD C,A IN A,(SLOT3) LD B,A LD A,SYS_PAGE OUT (SLOT3),A LD H,high SYS_PAGE.RAMD_KEYS; - #4000 - #4000 LD (HL),0 LD A,B OUT (SLOT3),A LD A,C ;JR EMM.FreeMem ;------[ ; Освободить блок памяти ; Вход: A - НАЧАЛО ЦЕПОЧКИ ; НЕ ПОРТИТЬ DE!!! ;EMM_FN3M: EMM.FreeMem: AND A SCF RET Z ; LD L,A CP ZX_RAM_ID CALL Z,SET_ROM_FLAG_ZX ; IN A,(SLOT3) EX AF,AF' LD A,SYS_PAGE OUT (SLOT3),A LD H,high (SYS_PAGE.RAMD_FAT) LD A,L EMM_F3M_L1: LD L,A LD A,(HL) ; следующий блок AND A JR Z,EMM_FN3M_ERR LD (HL),0 ; Освободить CP #FF ; Если не конец JR NZ,EMM_F3M_L1 ; продолжать EX AF,AF' OUT (SLOT3),A XOR A ; no error code RET EMM_FN3M_ERR: EX AF,AF' OUT (SLOT3),A LD A,BIOS.Error.BadNumber SCF RET ;----------------------------------------------------------------------; ; ; WARNING!!! не трогать DE ;----------------------------------------------------------------------; ; Получить страницу N ключа K ; Вход: A - КЛЮЧ RAM-Disk, B - номер страницы ; Выход: A - страница EMM.GetMemPageRMD: CALL GET_RAMD_ST RET C SCF RET Z ;JR EMM.GetMemPage ;------[ ; Получить страницу N блока K ; Вход: A - блок, B - номер страницы ; Выход: A - страница, IF CF - A=0 - нет блока, A=FF - END ;EMM_FN4M: EMM.GetMemPage: LD L,A IN A,(SLOT1) EX AF,AF' LD A,SYS_PAGE OUT (SLOT1),A INC B LD H,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) EMM_F4M_L1: LD A,(HL) AND A JR Z,EMM_F4M_ERR DEC B JR Z,EMM_F4M_END LD L,A CP #FF JR NZ,EMM_F4M_L1 EMM_F4M_ERR: LD L,A EX AF,AF' OUT (SLOT1),A LD A,L SCF RET EMM_F4M_END: EX AF,AF' OUT (SLOT1),A LD A,L AND A RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; Получить следующую страницу по FAT ; Вход: A - страница ОЗУ ; Выход: A - следующая страница ОЗУ ;EMM_FN5: EMM.GetMemPageNext: LD L,A AND A SCF RET Z IN A,(SLOT1) LD H,A LD A,SYS_PAGE OUT (SLOT1),A LD A,H LD H,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) LD L,(HL) OUT (SLOT1),A LD A,L AND A SCF RET Z AND A RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; Получить список страниц блока в HL ; Вход: A - блок, HL - адрес буфера - 256 байт. ; Выход: HL - адрес блока, B - длина блока в страницах ОЗУ ;EMM_FN5M: EMM.GetMemBlkPages: PUSH DE PUSH HL EX DE,HL LD B,0 LD L,A .loop: LD A,L LD (DE),A INC DE AND A JR Z,.error CP #FF JR Z,.end ; IN A,(SLOT1) LD C,A LD A,SYS_PAGE OUT (SLOT1),A LD H,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) LD L,(HL) LD A,C OUT (SLOT1),A ; INC B JR NZ,.loop .error: SCF .end: POP HL POP DE RET ; RAMDRV.EXE вызывает эту функцию с адресом в SLOT1, поэтому лучше так не оптимизировать ; EMM.GetMemBlkPages: ; PUSH DE ; PUSH HL ; EX DE,HL ; LD B,0 ; LD L,A ; IN A,(SLOT1) ; LD C,A ; LD A,SYS_PAGE ; OUT (SLOT1),A ; .loop: LD A,L ; LD (DE),A ; INC DE ; AND A ; JR Z,.error ; CP #FF ; JR Z,.end ; ; ; LD H,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) ; LD L,(HL) ; INC B ; JR NZ,.loop ; .error: SCF ; .end: LD A,C ; OUT (SLOT1),A ; POP HL ; POP DE ; RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; !FIXIT SAFE_RGADR ;RAMD_R_W: ; ╔════════════════════════════════════════════════╗ ; ║ RD/WR SECTOR ║\ ; ║ HL - BUFER ║\ ; ║ DE - ABS sector в 256b блоках ║\ ; ║ B - число данных в 256b блоках ║\ ; ║ A - block RAM ║\ ; ║ A' - команда чтение/запись/чтение ROM Disk ║\ ; ║ 0 - read, 255 - write, 70 - read ROM DISK ║\ ; ║ ** NOT USED TR-DOS VARS ** ║\ ; ╚════════════════════════════════════════════════╝\ ; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ BLK_RD_WR: AND A SCF RET Z ; AND A INC B DEC B RET Z ; ; сохраняем состояние прерываний LD C,A LD A,R PUSH AF ; сохраняем PORT_Y IN A,(PORT_Y) PUSH AF LD A,#C0 OUT (PORT_Y),A ; LD A,C DI CALL .start EX AF,AF' ; восстанавливаем PORT_Y POP AF OUT (PORT_Y),A ; восстанавливаем состояние прерываний POP AF JP PO,.noInterrupts EI .noInterrupts: EX AF,AF' RET ; .start: EX AF,AF' AND A ; 0 - read JR Z,.reset_Z CP #FF ; write JR Z,.keep_Z CP 5 ; old read JR Z,.reset_Z CP 6 ; old write JR Z,.keep_Z CP #46 JP Z,ROM_DISK EX AF,AF' SCF RET .reset_Z: INC A ; set Z for WRITE .keep_Z: EX AF,AF' ; PUSH HL PUSH BC LD C,SLOT1 IN B,(C) LD H,A LD A,SYS_PAGE OUT (SLOT1),A LD A,H LD H,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) LD L,A INC D .RAMD_LOOP_D: DEC D JR Z,.NOT_FOUR_BLK LD L,(HL) LD L,(HL) LD L,(HL) LD L,(HL) JR .RAMD_LOOP_D .NOT_FOUR_BLK: LD A,E .loop: SUB #40 JR C,.NOT_ONE_BLK LD L,(HL) JR .loop .NOT_ONE_BLK: AND #3F LD D,A ; DE - ADRESS in RAM-Disk LD E,0 LD A,L ; L - текущий банк RAM-Disk OUT (C),B ; восстановить страницу POP BC ; длина данных POP HL ; адрес буфера BIT 7,H JR NZ,.BLK_PAGE1 LD C,SLOT3 IN C,(C) OUT (SLOT3),A SET 7,D SET 6,D JR .BLK_CONT1 .BLK_PAGE1: LD C,SLOT1 IN C,(C) OUT (SLOT1),A RES 7,D SET 6,D .BLK_CONT1: ; DE - RamDisk, HL - data EX AF,AF' JR Z,.NO_EX_RW1 ; WRITE EX DE,HL ; for READ .NO_EX_RW1: EX AF,AF' LD A,16 .BLK_LL1: DUP 16 LDI EDUP DEC A JR NZ,.BLK_LL1 EX AF,AF' JR Z,.NO_EX_RW2 ; WRITE EX DE,HL ; for READ .NO_EX_RW2: EX AF,AF' ; DEC C прокрутился 256 раз и вернулся INC B ; B уже уменьшился на 1, DEC B JP Z,.BLK_EXIT_1 BIT 6,D JP NZ,.BLK_CONT1 BIT 7,D JR Z,.BLK_PAGE3_X IN A,(SLOT1) LD E,A LD D,high (SYS_PAGE.RAMD_FAT - #4000 - #4000) LD A,SYS_PAGE OUT (SLOT1),A LD A,(DE) OUT (SLOT1),A LD DE,#4000 JP .BLK_CONT1 .BLK_PAGE3_X: IN A,(SLOT3) LD E,A LD D,high SYS_PAGE.RAMD_FAT LD A,SYS_PAGE OUT (SLOT3),A LD A,(DE) OUT (SLOT3),A LD DE,#C000 BIT 7,H JP Z,.BLK_CONT1 LD E,A LD A,C OUT (SLOT3),A IN A,(SLOT1) LD C,A LD A,E OUT (SLOT1),A LD DE,#4000 JP .BLK_CONT1 .BLK_EXIT_1: LD A,D DEC A RLCA LD A,C JR C,.BLK_EXIT_2 OUT (SLOT1),A RET .BLK_EXIT_2: OUT (SLOT3),A AND A RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ;**************************************** ;!!!!! READ_ROM_PAGE_X чтение последних ; 256 байтов ПЗУ в служебную страницу зачем-то ; READ_ROM_PAGE_X: ; LD DE,0 ; LD BC,#100 ; один сектор ; PUSH DE ; PUSH BC ; LD HL,#3F00 ; LD DE,#FF00 ; LD A,#1F ; last page ROM ; JR ROM_DISK.loopRead ;**************************************** ; Чтение с ROM-Disk ; HL - адрес, куда читать ; DE - номер сектора (считать по 256b сектор) ; B - число секторов ; A' - размер сектора (1 - 256b, 2 - 512 b) ROM_DISK: ; сохраняем состояние прерываний ;LD A,R EX AF,AF' LD C,0 ; счетчик DEC A JR Z,.loop ;JR Z,.start DEC A SCF RET NZ EX DE,HL ADD HL,HL EX DE,HL LD A,B ADD A,A LD B,A ;!!!!! RET C DI CALL .loop ; ; восстанавливаем состояние прерываний ; EX AF,AF' ; JP PO,.noInterrupts ; EI ; .noInterrupts: ; EX AF,AF' ; ; RET C AND A RR D RR E XOR A RET ; .start: ; DI ; CALL .loop ; ; восстанавливаем состояние прерываний ; EX AF,AF' ; JP PO,.noInterrupts2 ; EI ; .noInterrupts2: ; EX AF,AF' ; ; ; RET .loop: PUSH DE ; номер сектора PUSH BC LD A,E AND #3F ; ADRESS in ROM-Page PUSH AF ; сохранить адрес EX DE,HL ; DE - адрес буфера ADD HL,HL ADD HL,HL ; H - номер банки LD A,(ROM_DISK.Pages.Number) INC H CP H LD L,H LD H, high ROM_DISK.Pages.Number ; ROM-Disk pages! LD A,(HL) ; PAGE-ROM POP HL ; восстановить адрес в ROM-Page LD L,0 ; если далеко захотели - выход с ошибкой JR C,.errorExit ; ROM-Disk-end ; DE - буфер ; HL - адрес в ROM ; B - число секторов ; A - ROM-Page .loopRead: PUSH HL ; откуда PUSH DE ; куда LD HL,-.stackDepth - .readProcedure.size ; memory stack use! ADD HL,SP ; stack PUSH HL ; адрес программы .readProcedure LD DE,.readProcedure ; перенести программу на стек EX DE,HL LD BC,.readProcedure.size LDIR ; программа на стеке LD BC,#100 ; длина сектора RET ; исполнить программу .readProcedure, на стеке адреса буфера и ROM ; DE - next address ; HL - ROM address .readNext: POP BC ; число секторов INC C ; счетчик считанных секторов DEC B ; сектора кончились? JR Z,.normExit BIT 6,H ; чтение не закончено PUSH BC ; сохранить счетчики JR Z,.loopRead ; читать дальше POP BC POP HL ; номер сектора LD A,B LD B,0 ADD HL,BC LD B,A ; вычислить след.сектор EX DE,HL ; теперь HL - адрес, DE сектор, B - сколько еще читать JP .loop ; начать все снова! ; чтение закончено .normExit: POP HL ; сектор, откуда велось чтение ADD HL,BC ; по возврату: HL - след.адрес EX DE,HL ; DE - след.сектор AND A RET ; ошибка .errorExit: ; !TODO сделать, чтоб на выходе показывалось количество прочитанных секторов POP BC POP DE SCF RET ; процедура, переносимая на стек для чтения из ROM-Disk ; осторожнее с PUSH, если надо много, то увеличивай .stackDepth .readProcedure: POP DE ; куда POP HL ; откуда OUT (ROM.SLOT0),A ; ROM_PAGE LDIR ; здесь читается из ROM-Disk LD B,A XOR A OUT (ROM.SLOT0),A OUT (CNF_PORT.ON),A LD A,B JP .readNext .stackDepth EQU 8 ; расстояние от конца процедуры до вершины стека. .readProcedure.size EQU $-.readProcedure ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; вход: DE - ram_disk trk_sec, A - RAM-Disk ; выход: HL - адрес, A - page RAMD_CALC_PAGE: CP 16 CCF RET C ; НЕТ ТАКОГО RAM-Disk PUSH AF LD H,D LD L,E ADD HL,HL ADD HL,HL LD B,H LD A,E OR #C0 LD C,A ; C - часть адреса, B - страница POP AF CALL EMM.GetMemPageRMD ; ПОЛУЧИТЬ СТРАНИЦУ LD L,0 LD H,C RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; Вход: ; A - RAM Disk ID ; Выход: ; A - Number (0..15) GET_RAMD_NUM: EX AF,AF' IN A,(SLOT1) EX AF,AF' LD BC,SYS_PAGE * 256 + SLOT1 OUT (C),B LD HL,SYS_PAGE.RAMD_KEYS - #4000 - #4000 LD BC,SYS_PAGE.RAMD_KEYS.NUM CPIR EX AF,AF' OUT (SLOT1),A EX AF,AF' SCF RET PE DEC L LD A,L SUB low SYS_PAGE.RAMD_KEYS RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; [x] 04/11/2023 ; Вход: ; B = 0: swap to Sp ; B = #FF: swap to ZX ; B = #FE: no swap, only get info ; Выход: ; A - current RAM Drives set SWAP_RAM_DRIVES: IN A,(SLOT3) EX AF,AF' PUSH AF ;на всякий случай LD A,SYS_PAGE OUT (SLOT3),A ; LD A,(SYS_PAGE.CURRENT_RAM_DRV) LD C,A CP B JR Z,.exit ; уже установлен запрашиваемый набор ; LD A,#FE CP B JR Z,.exit ; ничего не менять, выдать текущий набор ; .swap: LD HL,SYS_PAGE.RAMD_KEYS LD DE,SYS_PAGE.Sp_RAMD_KEYS LD B,SYS_PAGE.RAMD_KEYS.NUM .loop: LD A,(DE) LD C,A LD A,(HL) LD (DE),A LD A,C LD (HL),A INC HL INC DE DJNZ .loop ; LD A,(SYS_PAGE.CURRENT_RAM_DRV) XOR #FF LD (SYS_PAGE.CURRENT_RAM_DRV),A LD C,A ; .exit: EX AF,AF' OUT (SLOT3),A POP AF EX AF,AF' LD A,C RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; на выходе при А = 0 должен быть установлен флаг Z ; не убивает DE и BC GET_RAMD_ST: CP SYS_PAGE.RAMD_KEYS.NUM CCF RET C PUSH BC LD HL,SYS_PAGE.RAMD_KEYS - #4000 - #4000 ADD A,L LD L,A IN A,(SLOT1) LD B,A LD A,SYS_PAGE OUT (SLOT1),A LD C,(HL) LD A,B OUT (SLOT1),A LD A,C POP BC AND A RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; RAM-Disk A, BLK - B BLK_TO_RAMD: CP SYS_PAGE.RAMD_KEYS.NUM CCF RET C PUSH HL LD L,A IN A,(SLOT3) LD C,A LD A,SYS_PAGE OUT (SLOT3),A LD A,L LD HL,SYS_PAGE.RAMD_KEYS; - #4000 - #4000 ADD A,L LD L,A LD A,(HL) ; ключ блока AND A JR NZ,BLK_BUSY ; RAM-Disk занят - ошибка LD (HL),B LD A,C OUT (SLOT3),A LD A,B AND A POP HL RET ; BLK_BUSY: LD A,C OUT (SLOT3),A SCF POP HL LD A,L RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; RAM-Disk A RAMD_CLEAR: CP SYS_PAGE.RAMD_KEYS.NUM CCF RET C PUSH HL LD L,A IN A,(SLOT3) LD C,A LD A,SYS_PAGE OUT (SLOT3),A LD A,L LD HL,SYS_PAGE.RAMD_KEYS; - #4000 - #4000 ; RAM-Disk свободен ADD A,L LD L,A LD B,A ; запомнить удаляемый рамдиск LD A,(HL) AND A JR Z,BLK_BUSY ; возврат с ошибкой LD (HL),0 LD A,C OUT (SLOT3),A AND A POP HL RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; разделить блок памяти на два блока ; A - блок, B - длина первого блока после разделения ; выход: A - блок 1, B - блок 2 EMM.DivMemBlocks: INC B DEC B SCF RET Z DEC B LD E,A CALL EMM.GetMemPage ; получить номер страницы блока RET C LD D,A IN A,(SLOT3) EX AF,AF' LD A,SYS_PAGE OUT (SLOT3),A LD H,high (SYS_PAGE.RAMD_FAT); - #4000 - #4000) LD L,D LD A,(HL) LD (HL),#FF LD B,A EX AF,AF' OUT (SLOT3),A LD A,E AND A RET ;----------------------------------------------------------------------; ; ; ;----------------------------------------------------------------------; ; слить два блока памяти в один ; А - блок 1, B - блок 2 ; выход: А - блок EMM.MergeMemBlocks: LD E,A IN A,(SLOT3) EX AF,AF' LD A,SYS_PAGE OUT (SLOT3),A LD H,high (SYS_PAGE.RAMD_FAT); - #4000 - #4000) LD L,E LD C,B LD B,0 .EMM_ADD_L: LD A,(HL) AND A JR Z,.EMM_ADD_ERR CP #FF JR Z,.EMM_ADD_NEXT LD L,A DJNZ .EMM_ADD_L .EMM_ADD_ERR: EX AF,AF' OUT (SLOT3),A SCF RET .EMM_ADD_NEXT: LD A,C AND A JR Z,.EMM_ADD_ERR LD (HL),A EX AF,AF' OUT (SLOT3),A AND A LD A,E RET ;----------------------------------------------------------------------; ;