Multiloader from Stellar Contour. Latest optimization.

ZXNet echo conference «code.zx»

From Aleksey Malov To All 20 December 2001

Hi, All! In general, I’m releasing the source code of the loader from our Stellar Contour demo to the masses. In it, Unlike the loader in Tryptomine Dream there are 3 degrees of optimization, not just one. In general, read the source, everything is explained there clearly. The source was created in ZXASM 3.10, so perhaps something will not work in other asmakhs, but I tried to use the most standard assembler features. Actually, the work with TRDOS itself is at the very end of the multiloader, that’s all the rest is the logic of the work. ;))) ═══════════════════ LOADER .t ══════════════════ ;(C) Vivid & Tigrr from Brainwave ;AFTER ASSEMBLY YOU CAN RUN ;EVERYTHING SHOULD WORK NORMALLY L3D13 EQU 0 L3D2F EQU 1 ;DB TYPE(L3D13,L3D2F),LAG_UP,LAG_NEXT,PORTION DRIVER MACRO:DB =1,=2,=3,=4:ENDM popytki equ 3;NUMBER OF READING ATTEMPTS ;ONE SECTOR, AFTER WHICH ;THE SECTORS WITH WILL BE READ ;NEXT TRACKS ;3 - JUST RIGHT. INTTAB equ #be00 INTSP equ INTTAB.h+1*257 ;WARNING, A LOADER IS REQUIRED ;FOR WORK THE PLATE IS SIZE 1 KILOBYTE sectb equ INTTAB-#100 ;PLATE 1[256] ADRTB EQU sectb-#300 ;PLATE 2[768] ;BEFORE AND AFTER LAUNCHING THE LOADER ;CAN BE USED AREA AREA ;PLATES FOR YOUR OWN PURPOSE org #6000 JP BEGIN BEGIN ;EXAMPLE OF USING THE LOADER: LD IY,#5C3A ;SET FOR DISK DRIVE A (POSSIBLE ;THROW OUT) LD A,0 LD C,1CALL #3D13 LD C,#18 CALL #3D13 ;INSTALLED THE LOADER ;DATA FOR THIS MUST BE AVAILABLE ;AT ADDRESS DRIVEA CALL INSTALL ;TURN ON INTS D.I. LD HL,INTTAB LD A,H LD I,A INC A SETINT1 LD (HL),A INC L JR NZ,SETINT1 INC H LD(HL),A IM 2 LD L,H ;DURING LOADER OPERATION ;THE HANDLER MUST BE ;PROCEDURE inter LD (HL),#C3 INC HL LD (HL),inter INC HL LD (HL),inter/256 EI ;LOAD 27 SECTORS FOR EXAMPLE ;From TRACK 1 AND SECTOR 0 LD HL,#4000;LOAD ADDRESS LD DE,#0100;ROAD AND SECTOR ;(CURRENT TRACK AND SECTOR ARE STORED ;IN VARIABLE #5CF4). LOADER ;DURING ITS OPERATION, IT UPDATES ;IT'S AS IT SHOULD BE. LD B,27 CALL IM2LOAD ;NOW YOU CAN TRY TO LOAD ;SEVERAL SERIOUS FILES AT A TIME ; ADR PAGE LENGTH ;1.#4000 #10 #10 ;2.#C000 #17 #18 ;3.#8000 #10 #08 LD HL,LOAD1DAT;WHAT AND WHERE WE SHIP LD DE,(#5CF4);HAG. DOR. AND SEC. CALL LOADGRP;LOADING GROUP ;FILES ;TURN OFF THE INFORMATION IM 1 ;LET'S EXIT RET ;MAKE A MINI SETUP THAT ;WOULD JUST PUT IN THE NECESSARY BYTES ;AT ADDRESS DRIVEA ; PAY SPECIAL ATTENTION TO THE PARAMETER ;LAG_UPPER, BECAUSE IT IS ENTERED THERE;VALUE EQUAL: ;(16-L)�F, WHERE L IS THAT BAYDA, ;WHICH WAS DISPLAYED IN RDS IN THE FIELD ;LAG TRACK, I.E.: ; ;RDS LAG_UPPER ;0 #00 ;1 #0F ;2 #0E ;.. ;#0F #01 ; ; ;WHY IS THIS? ;BECAUSE (FOR LAG1): ; ;TRK0: 0123456789ABCDEF ;TRK1: F0123456789ABCDE ; ;HERE THE NUMBER IS ENTERED IN LAG_UPPER ;SECTOR TRK1 WHICH IS _ABOVE_ ;0 SECTOR TRK0, I.E. #0F ; ;INCORRECTLY SET LAG_UPPER MAY ;BREAKINGLY SLOCK THE LOADER ; ; ; IN THE LAG_NEXT FIELD IN PRINCIPLE IT IS POSSIBLE ;PUT 0, IT DOESN'T GET MUCH SPEED ;IT DEPENDS HIGHLY. ; ; ;BENEFITS OF UPLOADING A GROUP OF FILES IN ;THAT HE READS THEM LIKE ONE BIG ;FILE, BUT EACH SECTOR OF EACH FILE ;READS IN HIS PLACE. THE WHOLE POINT IS: ;THAT THE LOADER USES SEVERAL LEVELS ;OPTIMIZATIONS: ; ;1. OPTIMIZATION AT SECTOR LEVEL ;I.E. IF DURING LOADING A SECTOR ;THE ERROR OCCURRED OR AN INTERRUPTION OCCURRED, ;LOADER WILL TRY TO READ THE NEAREST ;(IN THE MEANING OF DISK ROTATION TIME) ;BEHIND THERE IS ANOTHER UNREAD SECTOR WITH THE CURRENT- ;ON THE UPPER SIDE OR FROM THE TOP (IF THERE IS THERE) ;THERE ARE UNREAD SECTORS). ;2. OPTIMIZATION AT TRACK LEVEL ;IF ANY OF THE SECTORS IS NOT ;READ FOR popytki ATTEMPT, THEN ;UNREAD SECTORS WILL BE READ ;FROM THE NEXT (PREVIOUS) TRACKS. ;THOSE. PERIODICAL ;MOVE THE HEAD BACK AND HERE IN ;WITHOUT ONE FILE. GIVEN ;OPTIMIZATION DOES NOT HAVE ANY EFFECT ;IFFECTS ON NORMAL DISK DRIVES,;SINCE 3 ATTEMPTS HAPPEN ;READING ALL 32 SECTORS AT CURRENT. ;THE TRACK, BUT ON THE DISK DRIVES, SPEED ;ROTATIONS OF WHICH ARE "SYNCHRONIC" WITH FREQUENCY ;FRAME (INTERRUPTION COMES CONSTANTLY ;DURING THE SAME SECTOR) ;ALLOWS YOU TO INCREASE READING SPEED ;2-3 TIMES COMPARED WITH NO ;THE OPTIMIZATION. ; THIS OPTIMIZATION DOES NOT PLAY ANYTHING ;ROLES IF THE WHOLE FILE FITS ;HA ON THE SAME PHYSICAL TRACK. ; ;3. OPTIMIZATION AT THE FILE LEVEL. ;WORKS ONLY WHEN READING A GROUP ;CONTRACTIVE ;FILES. ESPECIALLY NOTICEABLE BENEFITS WHEN ;READING SEVERAL SMALL FILES ;SECTORS OF 10-15 EACH IN A CONTRACT, ; HERE BY "FILE" MEANS ;JUST A BLOCK OF SEVERAL (#01-#E0) ;CONTINUOUS SECTORS. ;LIMITATION: TOTAL FILE VOLUME ;IN ONE GROUP MUST NOT EXCEED ;#E0 (240) SECTORS (56 KILOBYTES). ;IF YOU NEED TO READ IT ;MORE #E0 SECTORS, I RECOMMEND ;CREATE 2 APPROXIMATELY THE SAME GROUPS ;FILES, EACH OF WHICH IS NOT LONGER ;#E0 SECTORS ;FOR EXAMPLE, YOU MUST READ THE FOLLOWING ;CONTINUOUS FILES: ; BANK ADDRESS LENGTH(SECTORS) ;1. #8000 #10 #80 ;2. #C000 #11 #40 ;3. #C000 #13 #40 ;4. #C000 #14 #40 ;5. #C000 #16 #40 ;6. #C000 #17 #40 ;-------------------- ; #1C0 = 2*#E0 ;THEN IT’S SMART TO MAKE 2 GROUPS: ; ;GROUP1 DB #80,#10:DW #8000 ; DB #40,#11:DW #C000; DB #20,#13:DW #C000 ;HALF ; ;3TH FILE ; DB 0 ;I.E. IN TOTAL #E0 SECTORS ; ;GROUP2 DB #20,#13:DW #E000;2ND HALF ; ;3TH FILE ; DB #40,#14:DW #C000 ; DB #40,#16:DW #C000 ; DB #40,#17:DW #C000 ; DB 0 ;AND LOAD THEM LIKE THIS: ; LD DE, TRACK AND SECTOR; POSSIBLE ; ;TAKE FROM (#5CF4) ; LD HL,GROUP1 ; CALL LOADGRP ; LD DE,(#5CF4) ; LD HL,GROUP2 ; CALL LOADGRP ; ; EVERYTHING IS AS SIMPLE AS 2 KOPENKS ; ;SO, YOU NEED ONLY 3 ENTRY POINTS ;1. INSTALL - INITIALIZING THE LOADER ; CALLED ONLY ONCE ; OR WHEN CHANGING THE DISK DRIVE ; (THE DISK DRIVE NUMBER IS TAKEN FROM ; TRDOS SYSTEM VARIABLES) ; AT DRIVEA ADDRESS MUST ; INFORMATION ABOUT 4 IS AVAILABLE ; DISK DRIVES (3D13 OR 3D2F, ; WHAT LAG_UP, LAG_NEXT AND SOFTWARE ; HOW MANY SECTORS SHOULD BE COUNTED AS ; ONCE IF LOADING VIA ; 3D13) ; ;2. IM2LOAD - LOADING A BLOCK OF SECTORS ; HL - ADDRESS ; D - TRACK (0..255) ; E - SECTOR (0..15) ; B - NUMBER OF SECTORS 1-#E0 ; IN THE VARIABLE PAGE IS LOCATED ; PAGE TO LOAD TO ; ;3. LOADGRP - LOADING SEVERAL ; SECTOR BLOCKS: ; HL - ADDRESS OF INFORMATION ABOUT BLOCKS ; D - TRACK ; E - SECTOR ; ; I RECOMMEND FOR SWITCHING PAGES ;USE LKL PROCEDURE:; AT THE ENTRANCE: A - WHAT TO THROW IN ;PORT #7FFD ;IT SAVES INTO THE PAGE VARIABLE ;WHAT WAS BROUGHT INTO PORT #7FFD ;EITHER USE YOUR PROCEDURE, BUT ;DUPLICATE TO PAGE VARIABLE ;VALUE ENTERED IN PORT. ; YOU CAN PLACE IN THE SCREEN VARIABLE ;SCREEN NUMBER (0 OR 8) ;ONLY PLEASE NOTE THAT IF YOU WILL BE SHIPPING ;VIA #3D13 (E.G. FROM RAMDISC), THEN ;SCREEN WILL BE SETTED BY DEFAULT ;INSIDE THE TRDOS ITSELF TO EQUAL #00 ; ;IN GENERAL, IT IS POSSIBLE TO CHANGE THE SCREEN ;REMOVE ALL. ;) ; ;DATA FOR LOADING THE WHOLE BLOCK ;CONSECUTIVE FILES, TOTAL SIZE ;WHICH DOES NOT EXCEED #E0 SECTORS. ;DB LENGTH_IN_SECTORS, PAGE:DW ADDRESS LOAD1DAT DB #10,#10:DW #4000 DB #18,#17:DW #C000 DB #08,#10:DW #8000 DB 0 ;Actually, LOADER PROCEDURES ;EVERYTHING IS PRETTY CHAMBER, BUT IT WORKS ;===================================================== ;TURN ON PAGE A LKL PUSH BC LD BC,#7FFD LD (PAGE),A OR 0 SCREEN EQU $-1 OUT(C),A POP B.C. RET ;CURRENT RAM PAGE PAGE DB #10 ;TYPE (0-3D13) ;LAG UP (0-F) ;LAG NEXT (0-F) ;SEC PER INT - NUMBER OF SECTORS-1 WHICH ;WILL BE READ BETWEEN PLAYS ;MUSON IN CASE TYPE=0. FOR ;RAMDISK MUST BE PUT 1, FOR ;SET SCREW 0, FOR REGULAR ;SET DISK DRIVE 7 ;I.E. FROM RAMDISC IN ONE INTERNATIONAL TIME ;READ 2 SECTORS, AND FROM SCREW 1. ;FROM A REGULAR DISK DRIVE VIA 3D13;THE WILL NOT HAVE TIME TO READ IT IN 1 INT, ;THEREFORE 8 SECTORS NEED TO BE PUT ;ONLY SO THAT YOU DO NOT BRAKE SO MUCH ;MUZON. DRIVEA: DB L3D2F,#0E,#02,#07 DRIVEB DB L3D2F,#0E,#02,#07 DRIVEC DB L3D2F,#0E,#02,#07 DRIVED DB L3D13,#00,#00,#01 ;2 SECTORS WHEN READING RAMDISK ;DRIVER SETUP INSTALL LD A,(23798) ;TEK. DRIVE AND 3 LD(drvnum),A ADD A,A ADD A,A ADD A,DRIVEA LD L,A ADC A,DRIVEA/256 SUB L LD H,A ;HL-SETTING TABLE LD A,(HL) ;TYPE INC HL LD (TYPE),A LD A,(HL);LAG UPPER TRACK INC HL LD (lagtrack),A LD A,(HL);LAG NEXT TRACK INC HL LD DE,sectors LD B,sectorsb-sectors SETLGNT AND #0F LD(DE),A INC DE,A DJNZ SETLGNT LD A,(HL);SECTORS PER INT INC A LD (GRP3D13),A RET ;int procedure inter ex (sp),hl ld(intjp+1),hl ex(sp),hl inc sp inc sp ld(intex+1),sp ldsp,INTSP push af push bc push de push hl exx ex af,af' push af push bc push de push hl push ix push iy LD A,(PAGE) PUSH AF ;ISR body;HERE MAY BE THE CALL OF MUSIC ETC. ;BY JUST BLINKING THE CURB LD A,0 CURCOLOR EQU $-1 INC A AND 7 LD (CURCOLOR),A OUT(254),A POP AF CALL LKL ;exit from ISR EITHER TO TRDOS OR TO ;RAM ld a,(intjp+2) cp #40 jr c, zabij inex pop iy pop ix pop hl pop de pop bc pop af exx ex af,af' pop hl pop de pop bc pop af intex ld sp,#3131 ei intjp jp #c3c3 zabij ;we are in trdos ld hl,(intex+1) dec hl dec hl ld(intex+1),hl ld hl,#3d2f ld(intjp+1),hl jr inex ;------------------------------------- ;TABLE SHOWING HOW MUCH ;SECTORS NEED TO SHIFT WHEN CHANGING- ;SHIPPING THE HEAD ON 1-8 TRACKS ;DIRECTLY DEPENDS ON SECTOR SHIFT ;HA ON TWO PHYSICAL TRACKS ;IN PRINCIPLE, IT IS CONFIGED NORMALLY, CHANGE ;NOTHING IS NEEDED ;WHEN MOVE FORWARD sectors db -1,0,1,2,3,4,5,6,7,8,9 ;WHEN MOVE BACK sectorsb db 2,1,0,-1,-2,-3,-4,-5,-6,-7,-8 esectors ;------------------------------------- ;read from disk via #3d13 ram_dsk ld (#5cf4),de push hl ld hl,(#5cc2);break protect ld hl,#c9f1ld (#5cc2),hl ex(sp),hl dermo push bc EI halt di im 1 push hl ld de,(#5cf4) ;READ MIN(GRP3D13,B) SECTORS LD C,8 GRP3D13 EQU $-1 LD A,B CP C JR C,$+3 LD B,C LD A,B LD(_READED),A ld C.5 PUSH BC call #3d13 POP B.C. pop hl ld a,h add a,B ld h,a im 2 ei pop bc LD A,B SUB 0 _READED EQU $-1 LD B,A JR NZ,dermo pop hl ld (#5cc2),hl im 2 ei ret ;IM 2 LOADER ;(C)2000-2001 VIVID & TIGGR FROM BW ;hl - read address ;de - track(0...255) & sector(0...15) ;b - number of sectors to read ;VARIABLE ramld EQUALS 0 WHEN READING ;THRU #3D2F, AND NOT 0, WHEN READING ;VIA #3D13 (FOR EXAMPLE FROM A SCREW OR ;RAMDISC) IM2LOAD LD (FDESCR+2),HL LD A,(PAGE) LD(FDESCR+1),A LD HL,FDESCR LD(HL),B JR LOADGRP FDESCRDS 5 ;LOADING A BLOCK OF CONSEQUENTIAL FILES LOADGRP PUSH HL LD B,0 LDGRP1 LD A,(HL) OR A JR Z,V.S.E. ADD A,B LD B,A INC HL,HL,HL,HL JR LDGRP1 VSE POP HL JP LOADER LD3D13 LD (#5CF4),DELD3D13_ LD A,(HL) OR A RET Z INC HL LD B,A LD A,(HL) INC HL CALL LKL LD E,(HL) INC HL LD D,(HL) INC HL PUSH HL EX DE,HL LD DE,(#5CF4) CALL ram_dsk POP HL JR LD3D13_ ;ЗАГРУЗКА ГРУППЫ ФАЙЛОВ ;DE-START TRACK ;HL-FILES DESCRIPTOR ;+0 - LENGTH (0-END OF TABLE) ;+1 - PAGE ;+2 - ADR LOADER ld a,1 TYPE equ $-1 or a jr z,LD3D13 EI ld a,(#5cd6) push af PUSH HL ld (trsec),de ld (#5cf4),de ld hl,(#5cc2);break protect ld (runfile+1),hl ld hl,#c9f1 ld (#5cc2),hl push bc push de call posit ;track d pop de pop bc ;СОЗДАЕМ ТАБЛИЦУ СЕКТОРОВ ld hl,sectb xor a mksectb ld (hl),a inc l jr nz,mksectb ld a,d rra sbc a,a and #10 or e ld l,a ld (startsec),a and #ef ;HОМЕР СЕКТОРА ПОД ГОЛОВКОЙ ld (secunh),a ld c,b ;ЗАПОЛHЯЕМ ТАБЛИЦУ ДОРОЖЕК ld de,tracktb m00 ld c,0 m0 ld (hl),popytki retrys equ $-1 inc l inc cdjnz m1 ld a,c ld (de),a jr m2 m1 ld a,l and 31 jr nz,m0 ld a,c ld (de),a inc de jr m00 ;-------------------------------------- m2 ld a,e sub tracktb256 ld (hitrk),a inc a ld (osttracks),a xor a ld (direct),a ld (lowtrk),a ld (curtrk),a ;ЗАПОЛHЯЕМ ТАБЛИЦУ АДРЕСОВ POP HL LD E,0 FILLADT LD A,(HL) INC HL OR A JR Z,F_OK EXA LD A,(HL) ;PAGE LD (FPG),A INC HL LD C,(HL) INC HL LD B,(HL) INC HL EXA EX DE,HL FILL1 LD H,ADRTB/256 LD (HL),0 FPG EQU $-1 INC H LD (HL),C INC H LD (HL),B INC L INC B DEC A JR NZ,FILL1 EX DE,HL JR FILLADT F_OK ;-------------------------------------- load0 ld hl,sectb secunh equ $-2 ;поиск ближайшего несчитанного сектора ;в таблице ld a,l and #e0 ld d,a sseek ld a,(hl) ld e,l or ajr nz,found ld a,l add a,0 lagtrack equ $-1 and #0f or d or #10;up side ld l,a ld a,(hl) or a jr nz,found ld a,e inc a and #0f or d ld l,a jr sseek found ld a,e inc a and #0f or d ld (second),a ld a,l ld (nowread),a ;waiting for you and 16 or 0 drive number equ $-1 or #2c xor #10 call outff ;was a snowflake для чтеия ld a,l and #0F ld e,a ;waiting for a snowflake ld a,l sub 0 startsec equ $-1 LD L,A LD H,ADRTB/256 LD A,(HL) INC H CALL LKL LD A,(HL) INC H LD H,(HL) LD L,A LD A,E call readsec;subject readsec a ;с текущей дорожки ;cf=1 slightly slightly ld hl,sectb nowread equ $-2 jp nc,readoc ;oшибка чтения: dec (hl) jr nz,load0;small size ;subjects ;Exclusive snowflake snowflakes ;секторов на данной дорожке.ld a,(retrys) ld(hl),a ld e,a ld a,l and #e0 ld l,a xor a ld b,32 reinit cp (hl) jr z,dontinit ld(hl),e dontinit inc l djnz reinit ;how many unread tracks ld a,0 osttracks equ $-1;tracks left. dec a ;last? jr z,load0 ;nowhere further ;let's see where to move. polzem ld a,255 direct equ $-1 or A ld a,0 curtrk equ $-1 ld b,a jr z,forward cp 0 lowtrk equ $-1 jr nz,nchdir xor a ld (direct),a ld a,b jr forward1 nchdir ;we are looking for the nearest path towards the street ;change track numbers add a,tracktb256 ld l,a adc a,tracktb/256 sub l ld h,a xor a ldc,a seeklow dec hl dec b inc c cp(hl) jr z,seeklow ld a,b ld(curtrk),a ;c-offset ld a,c add a,sectorsb-1 ld l,a adc a,0+(sectorsb-1)/256 sub l ld h,a ld a,b rrca rrca rrca ld e,ald a,(secunh) ADD A,(HL) and 15 add a,e ld (secunh),a nostbw ld a,b add a,a ld de,#1111 trsec equ $-2 add a,d ld d,a call posit jp load0 forward cp 0 hitrk equ $-1 jr nz,forward1 ld a,#ff ld (direct),a ld a,b jr nchdir forward1 add a,tracktb256 ld l,a adc a,tracktb/256 sub l ld h,a xor a ld c,a seekhi inc hl inc b inc c cp (hl) jr z,seekhi ld a,b ld (curtrk),a ;c-offset ld a,c add a,sectors-1 ld l,a adc a,0+(sectors-1)/256 sub l ld h,a ld a,b rrca rrca rrca ld e,a ld a,(secunh) add a,(hl) and 15 add a,e ld (secunh),a nostf ld a,(trsec+1) add a,b add a,b ld d,a call posit jp load0 ;БУФФЕР ДЛЯ ХРАHЕHИЯ ЧИСЛА СЕКТОРОВ ;HА ФИЗИЧЕСКОЙ ДОРОЖКЕ tracktb ds 8 readok ;пометили сектор как прочитанный ld (hl),0;increase the current sector and track ld hl,#5cf4 ld a,(hl) inc a and 15 ld(hl),a inc hl jr nz,$+3 inc(hl) ;reduce the number of sectors that ;must be read from this track ld a,(curtrk) ld b,a add a,tracktb ld l,a adc a,tracktb/256 subl ld h,a dec (hl) jp nz,load0;read further ;we check whether the read ;track last ex de,hl ld hl,osttracks dec (hl) ex de,hl jr z,konets;read ;completely ;compress the boundaries if necessary ld a,(lowtrk) cp b jr nz,nosqueezelow ldc,a xor a squeezel inc hl inc c inc b cp(hl) jr z,squeezel ld a,b ld (lowtrk),a xor a ld (direct),a jp polzem nosqueezelow ld a,(hitrk) cp b jp nz,polzem ldc,a xor a squeezeh dec hl inc c dec b cp(hl) jr z,squeezeh ld a,b ld (hitrk),a ld a,#ffld (direct),a jp polzem konets pop af ld (#5cd6),a runfile ld hl,#2121 ld (#5cc2),hl ret ;reset VG93 (once for crooked cars), ;or the first time you access this ;drive posit ld a,#3e or a jr z,track xor a;next time ld(posit+1),a ld a,(drvnum) or #3c call outff ld a,%00001000 call out1f call gundos track ld a,d srl a ld (pipisa),a ld c,#7f call outc ld a,%00011000 call out1f jr gundos outff ld c,#ff outc ld ix,#2a53 jr dos stepbkw ld hl,pipisa dec (hl) ld a,%01111100 jr out1f stepfw ld hl,pipisa inc(hl) ld a,%01011100 out1f ld ix,#2fc3 jr dos gundos ld ix,#3ef5 dos push ix jp #3d2f deby db 1;needed for polling #1f ;read sector with current. roads readsec if 0 ld hl,0 aaaa equ $-2 inc hl res 6,h ld (aaaa),hl ld a,(hl) add a,a ret endif ld (address),hl inc ald c,#5f;physical sector number call outc;to sector register xor a ld (#5cd6),a ld hl,retdos;actions for push hl ;for control ld hl,deby ;for push hl ;correctness ld bc,#017f ;reading ldhl,#2121 addresses equ $-2 ld ix,#2091 jr dos retdos ld a,10 ld (#5cd8),a ld de,0 pipisa equ $-1 ld ix,#2740 call dos ld a,(#5ccd) and %01111111 ret z ld a,(#5cd6) neg ret ENDCODE ═════════════════════════════════ Bye, All. WBR, Vivid^Brainwave.