title SETKEYS utility name ('SETKEYS') ; The AMSTRAD SETKEYS utility for PCW machines ; Disassembled by W.Cirsovius ; Call: SETKEYS file OS equ 0000h BDOS equ 0005h FCB equ 005ch _Conout equ 2 _String equ 9 _vers equ 12 _Open equ 15 _RdSeq equ 20 _SetDMA equ 26 _OSErr equ 255 _cr equ 32 RecLng equ 128 _Userf equ 30 KM.EXP equ 0d4h KM.KEY equ 0d7h CD.VERS equ 0e3h CPM22 equ 22h CPM31 equ 31h lf equ 0ah cr equ 0dh eot equ '$' eof equ 'Z'-'@' eos equ -1 ; End of table marker MSB equ 7 LoMask equ 00001111b ALL equ 11111111b ASCmask equ 00011111b _EO equ 5 _EX equ 4 _NM equ 3 _HX equ 2 _IG equ 0 COMRAM equ 0c000h jr SETKEYS db 'SETKEYS #116',cr,lf db 'Developed by Locomotive Software Ltd.',cr,lf db 'Copyright (C) 1985 ' db 'Amstrad Consumer Electronics PLC',cr,lf ; ; %%%%%%%%%%%%%%%%%%%%% ; %%% START SETKEYS %%% ; %%%%%%%%%%%%%%%%%%%%% ; SETKEYS: ld (UsrStk),sp ; Save callers stack ld sp,LocStk ; Get local stack call GetVersion ; Get version of machine call PrepFile ; Prepare file call c,ProcKeys ; Process key file if found ld sp,(UsrStk) ret ; ; Process the key file ; ProcKeys: ld a,c cp 3 ; Test CP/M 2.2 jp z,InvalEnv ; .. invalid ld hl,(BDOS+1) ; Get top of memory dec h ld (HI.RAM),hl ; .. save dec hl ld a,h cp HIGH COMRAM ; Test range jp c,InvalEnv ; .. less memory PK.loop: call GetNum ; Get number jr z,PK.noNum ; .. nope, must be expansion call GetNumKey ; Get translation jr PK.Close PK.noNum: ld hl,$EXPND call FindStr ; Find expansion inc l ; .. test found call nz,GetNum ; .. yeap, get number ld de,$BADNUM ; Load message call nz,GetToken ; Get token if ok PK.Close: call nz,FixNL ; Fix to end of line call z,FixChar ; Fix next character call z,ProcErr ; .. process error call .EOF? ; Test end of file jr z,PK.loop ; .. nope ret ; ; Expansion string ; On success reg L holds 0 ; reg H holds 0 ; Otherwise reg L holds -1 ; reg H holds -1 ; $EXPND: db 'E',eos,0,0 db eos,eos,eos $BADNUM: db 'Bad key number or expansion token',cr,lf,eot ; ; Get key translation ; ENTRY Reg L holds key number ; EXIT Zero set on invalid translation ; Reg DE holds error message ; GetNumKey: ld c,l ; Save key number call GetState ; Get shift state ld h,a ; .. save call GetNew ; Get value ld b,a ; Set new value ld de,$ILLKEY ret z ld d,h ; Set state call StoreKey ; .. store it or ALL ; Set success ret $ILLKEY: db 'Bad shift or translation character',cr,lf,eot ; ; Get shift state ; EXIT Accu holds selected state ; GetState: push hl xor a ; Clear entry state GS.loop: ld hl,$CTRL call FindStr ; Find control prexix or l ; .. insert it inc h ; Test ready jr nz,GS.loop ; .. nope pop hl or a ; Test any state ret nz ; .. yeap ld a,00000001b ; Return normal ret ; ; Control string prefix ; On success reg L holds shift state ; reg H holds 0 ; Otherwise reg L holds 0 ; reg H holds -1 ; $CTRL: db 'C',eos,00010000b,0 db 'E',eos,00010000b,0 db 'N',eos,00000001b,0 db 'S',eos,00000010b,0 db 'A',eos,00000100b,0 db 'SA',eos,00001000b,0 db eos,0,eos ; ; Process token ; EXIT Zero set on error ; GetToken: ld d,l ; Save token ld hl,(HI.RAM) ; Get high memory call BuildToken ; .. build token ld b,d ; Get token number ld de,$BADEXPN ret z ; .. error string call StoreToken ; Store token ld de,$BADSTR sbc a,a ; Fix zero (CY ->> no ZERO) ret $BADEXPN: db 'Bad expansion string',cr,lf,eot $BADSTR: db 'String too long or token number invalid' db cr,lf,eot ; ; Process error ; ENTRY Reg DE points to error message ; ProcErr: push bc push af call String ; Print error call GetAttr ; Test EOL or EOF jr nz,PE.ex ; .. yeap ld de,$IGNORE call String ; Tell ignoring jr PE.go PE.loop: ld a,c call Conout ; Print character PE.go: call GetChr ; Get character and attribute call GetAttr ; Test EOL or EOF call Get ; Get character from file jr z,PE.loop ; .. there is more to print ld de,$CRLF call String ; .. close line PE.ex: pop af pop bc ret $IGNORE: db 'Ignoring rest of line: ',eot $CRLF: db cr,lf,eot ; ; Tell invalid environment ; InvalEnv: push de ld de,$INVENV call String ; Print error pop de ret $INVENV: db 'This program will not run in this environment' db cr,lf,eot ; ; Tell file not found ; OpenErr: push de ld de,$CANNOT call String ; Print error pop de ret $CANNOT: db 'Cannot open file',cr,lf,eot ; ; Print string on console ; ENTRY Reg DE points to string closed by '$' ; String: push hl push de push bc push af ld c,_String call BDOS ; .. print pop af pop bc pop de pop hl ret ; ; Print character on console ; ENTRY Accu holds character ; Conout: push hl push de push bc push af ld e,a ld c,_Conout call BDOS ; .. print pop af pop bc pop de pop hl ret ; ; Get new key value ; EXIT Accu holds value ; Zero flag set on error ; GetNew: push bc call ChrGet ; Get valid character pop bc call STRING? ; Test string ret z ; .. should be call GetStrVal ; Get value jp z,UnGet ; Unget character on error push af call STRING? ; .. fix for end pop af ret ; ; Build token ; ENTRY Reg HL points to location in COMMON memory ; EXIT Zero set indicates error ; BuildToken: push hl call ChrGet ; Get valid character call STRING? ; Test string jr z,BT.ex ; .. should be ld c,0 ; Clear count jr BT.go BT.loop: ld (hl),a ; Store character inc hl ; .. bump pointer inc c ; .. and count BT.go: call GetStrVal ; Get string value jr nz,BT.loop call STRING? ; Fix for end or 1 BT.ex: pop hl ret ; ; Get string value ; EXIT Accu holds value ; Zero flag set on end ; GetStrVal: push hl push de push bc jr GSV.go GSV.loop: call Get ; Get character from file GSV.go: call GetChr ; Get character and attribute ld a,b or NOT (1 SHL _EO) ; Check for EOL or EOF inc a jr z,GSV.ex ; .. yeap ld a,c cp ' ' jr c,GSV.loop ; .. skip control cp '"' ; Test empty string jr z,GSV.ex cp '^' call Get ; Get next character from file call z,GetStrByte ; Process prefix call z,UnGet ; Unget character GSV.ex: pop bc pop de pop hl ret ; ; Get byte from string ; EXIT Accu holds byte ; Zero flag set on invalid control ; GetStrByte: call GetChr ; Get character and attribute ld a,c cp '^' ; Test control prefix jr z,GSB.skp cp '"' ; .. test string prefix jr z,GSB.skp cp '@' ; Test control jr c,GSB.Numb? and ASCmask ; .. mask what we got GSB.skp: call Get ; Get character from file jr GSB.ex GSB.Numb?: cp '''' ; Test numeric value jr z,GSB.Numb ; .. yeap xor a ; Set error ret GSB.Numb: call Get ; Get character from file call GetNum ; Get number call z,GetKeyWrd ; .. nope, try key word jp z,UnGet ; Unget character if invalid call GetChr ; Get character and attribute ld a,c cp '''' call z,Get ; Get character from file ld a,l ; Get byte GSB.ex: ld b,0 inc b ; Fix success ret ; ; Get control keyword ; EXIT Reg L holds code on success ; Zero flag set on error ; GetKeyWrd: ld hl,$KEYWRD call FindStr ; Find keyword ld a,h inc a ; .. fix for result ret ; ; Keywords of control strings ; On success reg L holds control code ; reg H holds 0 ; Otherwise reg L holds 0 ; reg H holds -1 ; $KEYWRD: db 'NUL',eos,'@'-'@',0 db 'SOH',eos,'A'-'@',0 db 'STX',eos,'B'-'@',0 db 'ETX',eos,'C'-'@',0 db 'EOT',eos,'D'-'@',0 db 'ENQ',eos,'E'-'@',0 db 'ACK',eos,'F'-'@',0 db 'BEL',eos,'G'-'@',0 db 'BS',eos,'H'-'@',0 db 'HT',eos,'I'-'@',0 db 'LF',eos,'J'-'@',0 db 'VT',eos,'K'-'@',0 db 'FF',eos,'L'-'@',0 db 'CR',eos,'M'-'@',0 db 'SO',eos,'N'-'@',0 db 'SI',eos,'O'-'@',0 db 'DLE',eos,'P'-'@',0 db 'DC1',eos,'Q'-'@',0 db 'DC2',eos,'R'-'@',0 db 'DC3',eos,'S'-'@',0 db 'DC4',eos,'T'-'@',0 db 'NAK',eos,'U'-'@',0 db 'SYN',eos,'V'-'@',0 db 'ETB',eos,'W'-'@',0 db 'CAN',eos,'X'-'@',0 db 'EM',eos,'Y'-'@',0 db 'SUB',eos,'Z'-'@',0 db 'ESC',eos,'['-'@',0 db 'FS',eos,'\'-'@',0 db 'GS',eos,']'-'@',0 db 'RS',eos,'^'-'@',0 db 'US',eos,'_'-'@',0 db 'SP',eos,' ',0 db 'DEL',eos,7fh,0 db 'XON',eos,'Q'-'@',0 db 'XOFF',eos,'S'-'@',0 db eos,0,eos ; ; Test current character a string indicator ; EXIT Zero set if not string indicator ; STRING?: push bc call GetChr ; Get character and attribute ld a,c sub '"' ; Test string cp 1 sbc a,a call nz,Get ; Get character from file if so pop bc ret ; ; Get number from input ; EXIT Zero set if no number ; Reg HL holds number ; GetNum: push bc call ChrGet ; Get valid character bit _NM,b ; Test number jr z,GN.hex ; .. nope call GetDec jr GN.ex GN.hex: bit _EX,b ; Test hex prefix jr z,GN.ex ; .. nope call GetHex ; .. get it GN.ex: pop bc ret ; ; Get decimal number ; EXIT Reg HL holds number ; GetDec: push de push bc call ChrGet ; Get valid character bit _NM,b ; Test number jr z,GD.ex ; .. no ld hl,0 ; Init result jr GD.go GD.loop: ld e,l ; Copy number ld d,h add hl,hl ; * 2 add hl,hl ; * 4 add hl,de ; * 5 add hl,hl ; * 10 GD.go: ld a,c sub '0' ; Strip off offset add a,l ld l,a ; .. add to old number adc a,h sub l ld h,a call Get ; Get character from file call GetChr ; .. and attribute bit _NM,b ; Test still number jr nz,GD.loop ; .. yeap or 1 ; .. set success GD.ex: pop bc pop de ret ; ; Get hex number ; EXIT Reg HL holds number ; GetHex: push bc call ChrGet ; Get valid character bit _EX,b ; Test expansion prefix jr z,GH.ex ; .. nope call Get ; Get character from file call GetChr ; .. and attribute bit _HX,b ; Test hex call z,UnGet ; Unget character if not jr z,GH.ex ; .. and exit ld hl,0 ; Init result jr GH.go GH.loop: add hl,hl ; * 2 add hl,hl ; * 4 add hl,hl ; * 8 add hl,hl ; * 16 GH.go: ld a,c ; Get character and LoMask ; Mask lower part bit _NM,b ; Test 0..9 jr nz,GH.dec ; .. yeap add a,9 ; Fix for hex GH.dec: or l ld l,a ; Put into result call Get ; Get character from file call GetChr ; .. and attribute bit _HX,b ; Test still hex jr nz,GH.loop ; .. yeap or 1 ; Set success GH.ex: pop bc ret ; ; Fix for next character ; FixChar: call GetAttr ; Test EOL or EOF call nz,Get ; Get character from file if so ret ; ; Test current character end of file ; EXIT Zero set if not EOF ; .EOF?: jp EOF? ; .. only a hook ; ; Find string from table ; ENTRY Reg HL points to table ; EXIT Reg L holds 1st operand ; Reg H holds 2nd operand or -1 if not found ; FindStr: push de push bc push af call ChrGet ; Get valid character ld d,b ; Save attribute dec hl ; .. fix list jr FS.go FS..unget: inc e FS.unget: dec e call nz,UnGet ; Unget character jr nz,FS.unget FS.skip: ld a,(hl) ; Get character inc hl inc a jr nz,FS.skip ; .. skip item inc hl FS.go: ld e,0 ; Init counter jr FS.cmp FS.loop: call CmpChr ; Compare character jr nz,FS..unget ; .. not found call Get ; Get character from file inc e FS.cmp: call GetChr ; Get character and attribute inc hl ld a,(hl) cp eos ; Test end of string jr nz,FS.loop ; .. nope inc e dec e ; Test total end jr z,FS.end ; .. yeap ld a,b xor d ; Test same attribute and 00101011b ; Test result jr z,FS..unget ; .. still searching FS.end: inc hl ld e,(hl) ; Get operands inc hl ld d,(hl) ex de,hl ; .. into right reg pop af pop bc pop de ret ; ; Fix to end of line ; FixNL: push af FN.loop: call GetAttr ; Test EOL or EOF call Get ; Get character from file jr z,FN.loop ; .. get more till end pop af ret ; ; Compare characters ; ENTRY Accu and reg C hold characters ; EXIT Zero set if same ; CmpChr: sub c ; Compare ret z ; .. found cp -' ' ; Test blank ???? ret nz ; .. nope ld a,b or 11111101b ; Fix attribute inc a ; .. fix result ret ; ; Get valid definition character from file ; ChrGet: jr .ChrGet ; .. skip reading from file CG.loop: call Get ; Get character from file .ChrGet: call GetChr ; Get character and attribute bit _EO,b ; Test end of file or line ret nz ; .. exit bit _IG,b ; Test valid character jr nz,CG.loop ; .. nope ret ; ; Prepare the key file ; EXIT Carry set if file found ; PrepFile: push hl push de push bc xor a ld (RecPtr),a ; Clear pointer ld hl,Buff1 ; Init buffer ld (BufPtr),hl xor a ld (FCB+_cr),a ; Clear current record ld de,FCB ld c,_Open call BDOS ; Find file cp _OSErr ; Test file here call nc,OpenErr ; .. nope pop bc pop de pop hl ret nc ; .. exit on error ; ; Get character from file ; Put result into memory ; Get: push hl push de push bc push af ld a,(RecPtr) ; Get pointer or a jr nz,Get.Buff ; .. not empty ld hl,(BufPtr) ; Get pointer ld de,Buff1 ; Test position sbc hl,de jr nz,Get.mem ; .. not at start xor a ld (FCB+_cr),a ld de,Buff1 ld c,_SetDMA call BDOS ; Set disk buffer ld de,FCB ld c,_RdSeq call BDOS ; Read record or a ; Test more jr z,Get.more1 ; .. yeap ld a,eof ld (Buff1),a ; Set end of file Get.more1: ld a,RecLng ld (RecPtr),a ; Set buffer filled jr Get.Buff Get.mem: ld a,l cp RecLng ; Test at start of 2nd buffer jr z,Get.at2nd ; .. yeap ld bc,RecLng ld de,Buff1 ld hl,Buff2 ldir ; Unpack record Get.at2nd: ld de,Buff2 ld c,_SetDMA call BDOS ; Set 2nd buffer ld de,FCB ld c,_RdSeq call BDOS ; .. read it or a ; Test end of file jr z,Get.more2 ; .. nope ld a,eof ld (Buff2),a ; Set end of file Get.more2: ld a,RecLng ld (RecPtr),a ; Set buffer filled ld hl,Buff2 ld (BufPtr),hl ; Set other buffer Get.Buff: ld hl,(BufPtr) ; Get buffer ld c,(hl) ; .. fetch character ex de,hl call PutChr ; Save character and attribute ld a,c cp eof ; Test end of file jr z,Get.eof ; .. yeap ld a,(RecPtr) dec a ; Fix pointers if not ld (RecPtr),a ex de,hl inc hl ld (BufPtr),hl Get.eof: pop af pop bc pop de pop hl ret ; ; Unget current character ; UnGet: push hl push de push bc push af ld a,(Char) ; Get character cp eof ; Test end of file ld hl,(BufPtr) jr z,UG.dummy ; .. yeap, skip ld hl,RecPtr inc (hl) ; Fix pointers if not ld hl,(BufPtr) dec hl ld (BufPtr),hl UG.dummy: dec hl ld c,(hl) call PutChr ; Save character and attribute pop af pop bc pop de pop hl ret ; ; Put character and attribute into memory ; ENTRY Reg C holds character ; PutChr: bit MSB,c ; Test 80..FF ld b,01h jr nz,.PutChr ; .. yeap, set standard ld b,0 ld hl,AttrTable add hl,bc ld b,(hl) ; Fetch attribute .PutChr: ld (Char),bc ; Save character and attribute ret ; ; Get current character and attribute ; EXIT Reg C holds character ; Reg B holds attribute ; GetChr: ld bc,(Char) ; Get character and attribute ret ; ; Get EOL or EOF state of current character ; EXIT Zero set if not EOL or EOF ; GetAttr: ld a,(Attr) ; Get attribute and 1 SHL _EO ; .. test bit ret ; ; Test current character end of file ; EXIT Zero set if not EOF ; EOF?: ld a,(Char) ; Get character sub eof ; Fix for end of file cp 1 sbc a,a ret ; ; Get version of machine ; EXIT Reg C holds type of machine ; 0 : CPC6128 ; 1 : PCW8256 ; 3 : CP/M 2.2 version ; 4 : Other machine ; GetVersion: ld c,_vers call BDOS ; Get OS version cp CPM31 ; Test CP/M+ jr z,Vers31 cp CPM22 ; Test CP/M 2.2 ld c,4 ret nz ld c,3 ret Vers31: ld hl,(OS+1) ld de,3*(_Userf-1) add hl,de ld (Userf+1),hl ; Set USERF vector call GetType ; Get machine type ld c,a ; .. set result ret ; Userf: jp $-$ ; ; Get type of machine ; EXIT Accu holds type of machine ; GetType: call Userf ; .. get type dw CD.VERS ret ; ; Store key translation ; ENTRY Reg C holds number of key to be set ; Reg D holds key state ; Reg B holds new value for the key ; StoreKey: call Userf ; .. set translation dw KM.KEY ret ; ; Store token string ; ENTRY Reg B holds token number ; Reg C holds length of string ; Reg HL points to string in COMMON memory ; EXIT Carry set indicates success ; StoreToken: call Userf ; .. set translation dw KM.EXP ret ; ; Attribute table of all ASCII characters ; Bit definitions: ; ; 7 6 5 4 3 2 1 0 ; +----+----+----+----+----+----+----+----+ ; | xx | xx | EO | EX | NM | HX | AN | IG | ; +----+----+----+----+----+----+----+----+ ; ; xx N.C. ; EO EOL or EOF ; EX Extension string ; NM Numeric 0..9 ; HX Hex range A..F ; AN Alphanumeric prefix ; IG Ignore for definition ; AttrTable: ; ; Control characters ; db 01h,01h,01h,01h,01h,01h,01h ; 00..06 db 01h,01h,01h,01h,01h,01h,21h ; 07..0D db 01h,01h,01h,01h,01h,01h,01h ; 0E..14 db 01h,01h,01h,01h,01h,21h,01h ; 15..1B db 01h,01h,01h,01h ; 1C..1F ; ; ASCII characters ; db 01h,01h,00h,10h,01h,01h,10h ; !"#$%& db 01h,01h,01h,01h,01h,01h,01h ; '()*+,\ db 00h,01h,0ch,0ch,0ch,0ch,0ch ; ./01234 db 0ch,0ch,0ch,0ch,0ch,01h,01h ; 56789:; db 01h,01h,01h,01h,01h,06h,06h ; <=>?@AB db 06h,06h,06h,06h,02h,02h,02h ; CDEFGHI db 02h,02h,02h,02h,02h,02h,02h ; JKLMNOP db 02h,02h,02h,02h,02h,02h,02h ; QRSTUVW db 02h,02h,02h,01h,01h,01h,01h ; XYZ[\]^ db 01h,01h,06h,06h,06h,06h,06h ; _`abcde db 06h,02h,02h,02h,02h,02h,02h ; fghijkl db 02h,02h,02h,02h,02h,02h,02h ; mnopqrs db 02h,02h,02h,02h,02h,02h,02h ; tuvwxyz db 01h,01h,01h,01h,01h ; {|}~DEL db eof ds 412,0 UsrStk equ 1100h LocStk equ UsrStk+258 HI.RAM equ UsrStk+258 BufPtr equ HI.RAM+2 RecPtr equ BufPtr+2 Char equ RecPtr+1 Attr equ Char+1 Buff1 equ Attr+1 Buff2 equ Buff1+RecLng end