TD!$B`Magnolia software for 128k ram board for use with HZ-89/90 4    &  =RAMDSKMMCOM WARMBOOTCOM DEFAULT COMBOOTINV COMB318 HEXM318B ASM[ M318A HEXM318B HEXBANKSW ASMVLINK COMTEST128KCOMREAD ME RAMDISK DOC"0 C1!~XO#P~APPP2$2$# P~:P# PP~_Y2%2%>2&Ö  _APPP2$2$ _Y2%2%>2&*#~°#~°Fwx# ¶!~# y~ # 7 Invalid Argument, Ignored$8 Enter Logical Drive Name => $ Initialize Directory? $d ̏'7SZbwi y2(:$ :&&!2&!hi`")i`+"+i`"-:(O:$ > :(O:$ 24(:[-:4("0S2*0':4!'[2*5!9"51![) { B[+[R0))))))) 2/!/:g˶:g: 2g!a2:% #:2/!6#  *5  .x o? 48072182BJT WARMBOOT v2.240 $s1   2 :ʭ::­:A@O<ʷ2r!"!"p[O!·*"*p#"p} \OO<ʲ!"!"p[O"²*"*p#"p} O*.s!"s ^#fk^>2l2kK|:kOv:lOy * "Bn!l4{)6+4)!":O=Os ^#fk^ ~2m>2l2kK|:kOv:lOy͂n* "B!l4{ҙ6+4:m™  ß:_ *.<"~"9much as 100 times faster than floppy disks, RAMDISK makes it possible to greatly speedup disk intensive operations such as sorting, assembling, compiling, and editing. HARDWARE/SOFTWARE REQUIREMENTS - RAMDISK will work on any H/Z-89/90 equipped with the Magnolia 128K RAM board and ANY CP/M, including the Heath/Zenith, Magnolia and C.D.R. CP/Ms. RAMDISK MUST reside in the top 1K of system memory, starting at address 0FC00H. Therefore, you must re- SYSGEN your CP/M boot  RAMDISK - Version 2.0 By: Livingston Logic Post Office Box 5334 Pasadena, Calif. 91107 For: Magnolia Microsystems 2264 15th Avenue, West Seattle, Washington 98119 INTRODUCTION - RAMDISK Version 2.0 is an improved version of the universal "pseudo-disk" utility originally written for the Magnolia 128K RAM board for the H/Z-89/90 computers. This new version adds the capabilities to change the RAMDISK logical drive  @,i0))))))) 2/!/:g˶:g: 2g!a:% #:2/!6#  *5  .x o? 48 $$."":O=o~2*#^#V~n6[#s#rB 6[#s#r*$$.}":*$$.w!<~6(2|!|~Gw 2<:_ {8 ß This program causes MMS CP/M to warm boot from the drive specified as shown: WARMBOOT d: where d: is the drive name. After warm boot, the system may still need to read the directory of the logged-in drive, but the drive doesn't need to contain a valid copy of CP/M. $ Unable to write CCP.SYS to specifidisks with a 63K or smaller CP/M system. INSTALLATION - Run MOVCPM and SYSGEN to place a 63K (or smaller) system on all boot diskettes with which RAMDISK will be used. Refer to your CP/M documentation for details of how to use MOVCPM and SYSGEN. Once you have a bootable 63K CP/M disk, copy RAMDSKMM.COM to it using PIP, then reset the computer, and re-boot from the 63K system disk. You are then ready to take advantage of the incredible speed and flexibility of the RAassignment at any time, and also to re-initialize the RAMDISK directory at any time without re-booting. In addition, the drive assignment and directory initialization parameters may optionally be passed on the command line, allowing RAMDISK to be invoked by a SUBMIT file, or other auto-run facility. Once installed, RAMDISK gives you a super-fast 110K "disk drive" which can be used for any operations which you would normally use a "real" disk drive for. With access times as [-boot using the disk SYSGENed in the above steps. 5 - Execute RAMDISK.COM by typing "RAMDISK" in response to the CP/M "A>" prompt. 6 - When RAMDISK prompts you for the "Logical Disk Address", enter the letter for the drive you wish the RAMDISK to be addressed as. This can be any letter from "A" to "P". 7 - RAMDISK will then ask you if you wish to initialize the RAMDISK. The first time you execute RAMDISK following power-up, you MUST respond "Y". This causes the RAMDISK directory to be ܁[ed drive $ Not enough room for BDOS on system tracks$ CCP.SYS NOT FOUND $CCP SYS f t<cified as shown: WARMBOOT d: where d: is the drive name. After warm boot, the system may still need to read the directory of the logged-in drive, but the drive doesn't need to contain a valid copy of CP/M. $ Unable to write CCP.SYS to specifi@!"!¨2":_ Program DEFAULT changes the default command drive specified by the last byte in the CCP. Called as: DEFAULT d: where d: specifies the new default drive. If '@:' is specified, the currently logged-in drive is the default. $ { ã CCP.SYS NOT FOUND $Changing Default Command Drive to .:$CCP SYS D! >6052682BJT DEFAULT v2.240 $s15  :ʛ::›:@@Qқ2  2 <ʨ2!"!¨2":_ Program DEFAULT changes the default command drive specified by the last byte in the CCP. Called as: DEFAULT d: where d: specifies the new default drive. If '@:' is specified, the currently logged-in drive is the default. $ { ã CCP.SYS NOT FOUND $Changing Default Command Drive to .:$CCP SYS k[ BANKC EQU 04H BANKD EQU 24H BANKE EQU 10H BANKF EQU 30H BANKG EQU 14H BANKH EQU 34H *** ** LINKS TO REST OF SYSTEM *** PATCH EQU BASE+1600H MBASE EQU BASE ; MODULE BASE COMBUF EQU BASE+0C000H ; COMMON BUFFER BUFFER EQU BASE+0F000H ; MODULE BUFFER GETCHR EQU PATCH+303H PUTCHR EQU PATCH+306H *** ** PAGE ZERO ASSIGNMENTS  ***; 9/2/82 10:35 BJT ** CP/M DISK I/O ROUTINES ** ** ** **'* ****** MACRO ASSEMBLER DIRECTIVES **i TITLE 'Invisible Disk Driver' MACLIB Z80 ; USE Z80 LIBRARY $-MACRO ; DISABLE MACRO EXPANSIONS *** DW MODLEN,BUFLEN BASE EQU 0000H ; ORG FOR RELOC ***** PHYSICAL DRIVES ARE ASSIGNED AS FOLLOWS ***** ***** ***** ***** 40 112K INVISIBLE DISK DRIVE 3:040001FFFFFD :10228000C305000028E1310001E53A0400D628C06A :102290001100072100003A0300C6020E00B71FCB51 :1022A0001947EDB0F3AFD3D4D3E4D3DC3E03F680CB :1022B000D3D3D3E3D3DB210C007DD3D07CD3D12186 :1022C0000C007DD3E07CD3E12180017DD3D87CD389 :1022D000D93E03D3D3Dc3E3D3DB3E0FD3D4D3E4D35C :1022E000DCDBD5DBE5DBDDDBD0DBE0DBD8C3700797 :1022F0003E20D3F2310000C3001600 B1 :102300 CD :1023100 0BD :10232000  ** ORG 0 ?CPM: DS 3 ?DEV$STAT: DS 1 ?LOGIN$DSK: DS 1 ?BDOS: DS 3 ?RST1: DS 3 ?CLOCK: DS 2 ?INT$BYTE: DS 1 ?CTL$BYTE: DS 1 DS 77 ?FCB: DS 36 ?DMA: DS 128 ?TPA: DS 0 **** ** OVERLAY MODULE INFORMATION ON BIOS *** ORG PATCH DS 51 ; JUMP TABLE DSK$STAT: DS 1 STEPR: DS 1 ; MINI-FLOPPY STEP RATE SIDED: DS 3 ; SIDE CONTROL FOR EACH DRIVE (0-SINGLE,1=DOUBLE) DS '! ***** ***** ***** *** *** ** PORTS AND CONSTANTS **|* GP EQU 0F2H ; GP PORT BIT$MASK EQU 0CAH ; MEMORY MAP BIT MASK ESC EQU 1BH CR EQU 0DH LF EQU 0AH BEL EQU 07H **)* ** MEMORY BANK CONSTANTS FOR MAPPING ** * BANKA EQU 00H BANKB EQU 20H { AD :1023300 09D :1023400 08D :1023500 07D :1023600 06D :1023700C51 :1023800000002 0010240EA :102390003D :00 2300 CD :1023100 0BD :10232000 ˩4 ; FOR EIGHT-INCH DRIVES MIXER: DB 40 DS 15 ; LOGICAL-PHYSICAL DRIVE TABLE DRIVE$BASE: DB 40,41 ; DRIVE MODULE BASE TABLE DW MBASE DS 28 TIME$OUT: DS 3 NEWBAS: DS 2 NEWDSK: DS 1 NEWTRK: DS 1 NEWSEC: DS 1 HRDTRK: DS 2 DMAA: DS 2  PAGE **,* ** START OF RELOCATABLE DISK I/O MODULE **c* ORG MBASE ; START OF MODULE JMP INIT ;INITIALIZE FIRST TIME, SELECT THEREAFTER JMP REBYT CAN HIT DIRECTORY OLDSP: DW 0 GPDATA: DB 0 MYBUF: CHRBFR: DB ESC,'q',ESC,'Y8 ',ESC,'E' ; NORMAL VID, CURSOR TO 25, CLEAR TRNMSG: DB ESC,']$$' ; EXTRA $ FOR y1 (DISABLE 25TH)INSERTED LATER RAMTEST: ; SEE IF 128K INSTALLED (WE'RE IN MAP B) SSPD OLDSP ; SAVE STACK FROM MEMORY SWAP LXI SP,STACK LXI H,0 ; PICK AN ADDRESS BELOW COMMON MOV B,M ; GET CONTENT MOV C,B ; SAVE A COPY PUSH B PUSH H CALL SETMEM ; TEST MAP E POP H POP B INR B ; CHANGE THE VALUE MOV D,M ; SAVE  DAD H ; *128 SHLD SECLOC RET SETMEM: ; MAP IN APPROPRIATE MEMORY BANK LXI H,DSKBNK DI ; DISABLE INTERRUPTS LDA ?INT$BYTE ; OLD GP PORT INFO STA GPDATA ; SAVE FOR RESTORATION LATER ANI BIT$MASK ; REMOVE MEMORY MAP BITS ORA M ; NEW MAP BITS OUT GP RET RESMEM: ; RESTORE ORIGINAL MEMORY CONFIGURATION LDA GPDATA ; OLD GP PORT INFO ANI 0FEH ; REMOVE LSB OUT GP STA ?INT$BYTE ; TO CLEAN UP INFORMATION EI ; AND ENABLE INTERRUPTS RET MOVMEM: LXI B,128 LDIR RK LXI SP,STACK ; AND GET A NEW ONE CALL CALCUL ; CALCULATE DISK PARAMETERS LHLD DMAA ; GET 128 BYTES FROM DMAA LXI D,MYBUF PUSH D CALL MOVMEM CALL SETMEM ; MAP IN APPROPRIATE BANK LDED SECLOC POP H CALL MOVMEM CALL RESMEM ; RESTORE MEMORY CONFIGURATION ; WREND: LSPD OLDSP ; RESTORE STACK POINTER XRA A ; NO ERRORS RET PAGE CALCUL: ; CALCULATE DISK PARAMETERS LHLD HRDTRK ; REQUESTED TRACK DAD H ; *2 DAD H ; *4 DAD H ; *8 DAD H ; *16 DAD H ; *32 LDAD$MEM JMP WRITE$MEM DB '77318 ',0,'Invisible Disk w/ System Space ',0,'2.242$' OFF EQU 3 ;NUMBER OF SYSTEM TRACKS DRM EQU 64-1 ;# OF DIRECTORY ENTRIES DIR EQU OFF*32*128+DRM*32 ; FIRST BYTE OF LAST DIR ENTRY MEM$BASE: DW 0,0,0,0,DIRBUF,DPB,CSVCM,ALV3 DPB: DW 32 ; SPT DB 3,7,0 ; BSH, BLM, EXM DW 112-(4*OFF)-1,DRM ; DSM, DRM DB 11000000B,0 ; AL0, AL1 DW 0,OFF ; CKS, OFF ** mode bytes DB 0,80H,0,0,80H,0 ; IGNORE MODE BYTE ; ; SELECT DISK FUNCTION ; SEL$MEM: MVI 4PRESENT CONTENT FROM MAP E MOV M,B ; STASH CHANGED VALUE MOV B,D ; SAVE OLD VALUE IN B PUSH B PUSH H ; SAVE CALL RESMEM ; SWITCH BACK TO B POP H POP B MOV A,M ; GET BACK PRESUMED ORIGINAL CMP C ; DOES IT MATCH? - IT SHOULD PUSH PSW ; SAVE RESULT PUSH B PUSH H CALL SETMEM ; E AGAIN POP H POP B MOV M,B ; RESTORE CONTENT IN MAP E CALL RESMEM ; BACK TO B POP PSW ; GET MATCH RESULT LSPD OLDSP ; GET READY TO RETURN STC CMC ; [NC] FOR NO ERROR RZ ; OK IF ET ; ; INITIALIZATION ONLY ; FILBYT: MOV M,A INX H DJNZ FILBYT CALL SETMEM ; MAP IN BANK C MVI B,(DRM+1)/4 ; GET WHOLE DIRECTORY NXBYT: PUSH B LXI H,MYBUF CALL MOVMEM POP B DJNZ NXBYT LXI D,DIR ; POINT TO LAST ENTRY OF DIRECTORY LXI H,CODE ; USER 31, THIS ENTRY = 'INITIALIZED ALREADY' LXI B,6 ; LENGTH OF CODE LDIR CALL RESMEM ; RESET TO PAGE 0 JMP WREND ; RESTORE THE STACK AND RETURN PAGE ; DATA STORAGE AND BUFFERS ; SECLOC: DW 0 DSKBNK: DB BANKE ; SO FILLA NEWSEC ; REQUESTED SECTOR MVI D,0 MOV E,A DAD D ; HL CONTAINS A SECTOR NUMBER PUSH H ; SAVE IT FOR LATER LXI D,56*8 ; SECTORS PER DISK DSBC D ; SEE IF ON PAGE 0 OR 1 POP D ; CLEAN UP STACK MVI A,BANKH ; DEFAULT TO BANK 2 JNC CALCADR XCHG ; ACTUAL SECTOR TO HL MVI A,BANKG ; IN BANK 0 CALCADR: ; CALCULATE THE ACTUAL ADDRESS STA DSKBNK ; NEW MEMORY BANK BITS DAD H ; TIMES 128 FOR ACTUAL ADDRESS DAD H ; *4 DAD H ; *8 DAD H ; *16 DAD H ; *32 DAD H ; *64  C,0 ; LOGICAL DRIVE NUMBER LXI D,MEM$BASE ; DISK PARM HEADER RET ; ; READ DISK FUNCTION READ$MEM: SSPD OLDSP ; SAVE CALLERS STACK LXI SP,STACK ; AND GET A NEW ONE CALL CALCUL ; CALCULATE DISK PARAMETERS CALL SETMEM ; MAP IN APPROPRIATE BANK LHLD SECLOC ; GET 128 BYTES FROM THE DISK LXI D,MYBUF PUSH D CALL MOVMEM CALL RESMEM ; RESTORE MEMORY CONFIGURATION POP H LDED DMAA CALL MOVMEM JR WREND ; ; WRITE DISK FUNCTION ; WRITE$MEM: SSPD OLDSP ; SAVE CALLERS STAC |MATCH LXI H,NOMEM ; ERROR MESSAGE STC RET ; RETURN [C] FOR ERROR INIT: ; PERFORM INITIALIZATION AND TESTS LXI H,SEL$MEM PUSH H ; 'RETURN' TO SELECT AFTER INIT SHLD MBASE+1 ; LATER SELECTS GO DIRECT LDA NEWBAS+1 ; PAGE ADDRESS OF DRIVER CPI 0E0H ; >= 56K? LXI H,PAGERR ; IF [C], USE MESSAGE CNC RAMTEST ; IF NO ERR, CHECK RAM JNC NOERR ; IF STILL NO ERR, CHECK INITIALIZATION ; ; CHRBFR MUST END HERE ; IF $-TRNMSG<80 REPT 80+TRNMSG-$ DB NOP ENDM ENDIF V,00100001B DB 00100000B,10010010B,01001000B,10010001B,00010010B,00100001B,00000001B,00CB DB 00000001B,00000100B,00000001B,00010000B,00100000B,00010000B,00B,00/B DB 01000001B,00100000B,00010000B,00010010B,00B,00 B,00001001B,00B DB 01000000B,00001000B,00000010B,00001000B,01000001B,00001000B,10010000B,10010010B DB 01001001B,00100100B,10010010B,00000010B,01000001B,00100000B,01000000B,01000000B DB 01001000B,10001001B,00100100B,01000000B,01000000B,10010010B,00000001B,0CNE, FLAG Z XIT: PUSH PSW ; SAVE RESULT CALL RESMEM POP PSW LSPD OLDSP ; GET OLD POINTER RET ; RETURN Z OR NZ; A<>'Y' CODE: ; THIS IS A "GRAY" PATTERN, CLEVERLY DISGUISED DB 00011111B,00111111B,00101111B ; AS A DIRECTORY ENTRY DB 00100111B,00100011B,00100010B ; IN USER 31 NOMEM: DB ESC,'E',BEL DB 'Memory map error!$' PAGERR: DB ESC,'E',BEL DB 'Invisible disk module below 56K boundary!$' PRSMES: DB ' - Press any key$' POSIT: DB ESC,'j',ESC,'x1',ESC,'p',ESC,'Y8 $' ; SAVE5TH LINE CALL PRINT RET ; ETC. RES25: ; RESTORE 25TH LINE LXI H,CHRBFR ; NORMAL VID, CURSOR TO BEGINING OF LINE 25 CALL PRINT ; AND RESTORE PREVIOUS MESSAGE, IF ANY LXI H,RESCSR ; RESTORE CURSOR CALL PRINT RET NOERR: ; NO PROB WITH ADDRESS OR RAM CALL PTEST ; WAS POWER DOWN, OR DISK NOT INITIALIZED? RZ ; IF INITIALIZED, GO TO SELECT MVI B,128 ; OTHERWISE, INITIALIZE DIRECTORY MVI A,0E5H ; FILL BUFFER WITH 0E5H LXI H,MYBUF LXI D,OFF*32*128 ; POINT TO DIRECTORY c; CALL GET25 ; GET 25TH, PRINT MESSAGE LXI H,PRSMES ; PRESS ANY KEY CALL PRINT CALL GETCHR ; WAIT FOR RESPONSE CALL RES25 ; RESTORE 25TH LINE, IF ANY LXI H,RDWERR ; INDICATE READ/WRITE ERROR. . SHLD MBASE+4 ; ON ALL READS. . SHLD MBASE+7 ; AND WRITES ; RETURN TO SELECT RDWERR: MVI A,1 ; SIGNAL ERROR ON R/W, OR MAKE (A) <> 'Y' ORA A ; SET FLAG RET GET25: ; SAVE 25TH LINE, SEND MESSAGE PUSH H ; SAVE MESSAGE LXI H,TRNMSG ; TELL TERMINAL TO TRANSMIT 25TH LINE CALL PR e0000010B DB 01000001B,00B,00000100B,00100000B,00B,00B,00B,00B DB 00B,00B,00B,00B,00B,00B,00B,00B DB 00B,00B,00B,00B,00B,00B,00B,00B DB 00B,00B,00B,00B,00B,00B,00B,00 B $+PRINT ** ** COMMON BUFFERS ** ORG COMBUF DS H CURSOR, ENABLE 25, ; POSITION, REV VIDEO RESCSR: DB ESC,'k$' REPT (($+0FFH) AND (0FF00H))-$ DB 0 ; FILL TO PAGE BOUNDRY ENDM STACK: DS 0 MODLEN EQU $-MBASE ZMODLEN EQU MODLEN PAGE ; ==# ; PROGRAM RELOCATION BIT MAP ; ==( $-PRINT DB 00100100B,10000000B,00B,00B,00B,00B,00B,00000001B DB 01010100B,00B,00)B,00000001B,00001001B,00100100B,10010001B SSPD OLDSP LXI SP,STACK GOFIL: JMP FILBYT PRINT: MVI A,'$' CMP M RZ MOV C,M PUSH H CALL PUTCHR POP H INX H JR PRINT PTEST: ; IF DISK NOT INITIALIZED, RETURN NZ LXI SP,STACK ; CURRENT SP SAVED PREVIOUSLY CALL SETMEM ; MAP IN DIRECTORY LXI H,DIR ; POINT TO DIRECTORY LXI D,CODE ; POINT TO TEST PATTERN MVI B,6 TSTLP: LDAX D ; THIS LOOP COMPARES STRINGS CMP M ; MATCH? JRNZ XIT ; IF NO MATCH, RETURN NZ INX D ; POINT TO NEXT BYTES INX H DJNZ TSTLP ; WHEN DOINT LXI H,'y1' ; DISABLES 25TH SHLD TRNMSG+1 ; ASSUME NO 25TH LINE CALL GETCHR CPI CR ; CHECK FOR A CARRIAGE RETURN JRZ NO25 ; IF 1ST IS CR, THEN NO 25TH LINE LXI H,TRNMSG ; ADDRESS FOR TRANSMITTED LINE BUFFER MORE: MOV M,A ; SAVE CHARACTER INX H ; POINT TO NEXT MVI M,'$' ; STRING TERMINATOR PUSH H ; KEEP INTERRUPT ROUTINE FROM KILLING THIS CALL GETCHR POP H CPI CR JRNZ MORE NO25: LXI H,POSIT ; POINT TO 25,ETC, FOR FOLLOWING MESSAGES CALL PRINT POP H ; MESSAGE TO 2 #20 DS 64 DS 2 DIRBUF: DS 128 ** ** BUFFERS **' ORG BUFFER CSVCM DS 0 ALV3 DS 14 **H BUFLEN EQU $-BUFFER END * ORG BUFFER CSVCM DS 0 ALV3 DS 14 +*** ** COMMON BUFFERS ** ORG COMBUF DS  23A0 :1002100010F8F5CDE900F1ED7B2201C91F3F2F2732 :1002200023221B45074D656D6F7279206D6170202B :100230006572726F7221241B4507496E7669736976 :10024000626C65206469736B206D6F64756C6520EA :1002500062656C6F772035364B20626F756E646116 :1002600072792124202D205S07265737320616E797C :10027000206B6579241B6A1B78311B701B59382051 :10028000241B6B24 00 A0 :1002900 05E :1002A00 04E :1002B0003E :1002C00000 1A000E1CDF500CDE900ED7B2201AFC92A741640 :1000B00029P3A731616005F19E511C0016B :1000C000ED52D13E34D2CB00EB3E1432210129292E :1000D00029221F01C9212101F33A0D00CB :1000E000322401E6CAB6D3F2C93A2401E6FED3F2BD :1000F000320D00FBC9018000EDB0C9772310FCCDA3 :10010000D9000610C5212501CDF500C110F611E07A :1001100007211C02010600EDB0CDE900C3A70000D5 :1001200000100000001B711B5938201B451B5D246B :100l1300024ED7322013100032100004648C5E5CDBE :10014000D900E1C104567042C5E5CDE900E1C17EA8 :10015000B9F5C5E5CDD90   #00 2E :1002D00 01E :1002E00 E :1002F000FE :10030000248001540000010924912114 :1003100020924891122101000104011020100000D8 :100320004120101200000900400802084108909284 :1003300049249202412040404889244040920102D1 :100340004100042 00 048 :100350009D :00  0E1C170CDE900F1ED7B80 :100160002201373FC821220237C9215B00E5220165 :10017000003A7016FEE0213702D43101D2D701CD0A :100180009B01216402CDEF01CD0319CDCA01219756 :10019000012204002207003E01B7C9E5212D01CD4F :1001A000EF01217931222E01CD0319FE0D281021F6 :1001B0002D0177233624E5CD0319E1FE0D20F3212F :1001C0007502CDEF01E1CDEF01C9212501CDEF0190 :1001D000218102CDEF01C9CDFC01C806803EE52199 :1001E0002501110000ED732201310003C3FB003E25 :1001F00024BEC84EE5CD0619E12318F3310003CD26 :10020000D90021E007111C0206061ABE200413 J:0400030E00EB :01163C002885 :04164C002829000049 :10000000C36A01C36100C38500373733313820002C :10001000496E76697369626C65204469736B202050 :100020002D202066696C6573206F6E6C792020002E :10003000322E3234322400;56C08E :10004000460000F000F020000307006F003F00C0F2 :1000500080000080000E001136004B :10006000C9ED732201310003CDAD00CDD9002A1FA7 :1000700001112501D5CDF500CDE900E1ED5B761646 :10008000CDF5001822ED732201310003CDAD002A19 :100090007616112501D5CDF500CDD900ED5B1F01F8 :1000 n   #00 2E :1002D00 01E :1002E00 E :1002F000FE :10030000248001540000010924912114 :1003100020924891122101000104011020100000D8 :100320004120101200000900400802084108909284 :1003300049249202412040404889244040920102D1 :100340004100042 00 048 :100350009D :00  0E1C170CDE900F1ED7B80 :100160002201373FC821220237C9215B00E5220165 :10017000003A7016FEE0213702D43101D2D701CD0A :100180009B01216402CDEF01CD0319CDCA01219756 :10019000012204002207003E01B7C9E5212D01CD4F :1001A000EF01217931222E01CD0319FE0D281021F6 :1001B0002D0177233624E5CD0319E1FE0D20F3212F :1001C0007502CDEF01E1CDEF01C9212501CDEF0190 :1001D000218102CDEF01C9CDFC01C806803EE52199 :1001E0002501110030ED732201310003C3FB003EF5 :1001F00024BEC84EE5CD0619E12318F3310003CD26 :10020000D90021E037111C0206061ABE200413 x:0400030E00EB :01163C002885 :04164C002829000049 :10000000C36A01C36100C38500373733313820002C :10001000496E76697369626C65204469736B2077F9 :100020002F2053797374656D2053706163652000D0 :10003000322E3234322400;56C08E :10004000460000F000F0200003070063003F00C0FE :10005003000080000080000E0011360048 :10006000C9ED732201310003CDAD00CDD9002A1FA7 :1000700001112501D5CDF500CDE900E1ED5B761646 :10008000CDF5001822ED732201310003CDAD002A19 :100090007616112501D5CDF500CDD900ED5B1F01F8 :1000   92370 :1002100010F8F5CDE900F1ED7B2201C91F3F2F2732 :1002200023221B45074D656D6F7279206D6170202B :100230006572726F7221241B4507496E7669736976 :10024000626C65206469736B206D6F64756C6520EA :1002500062656C6F772035364B20626F756E646116 :1002600072792124202D205S07265737320616E797C :10027000206B6579241B6A1B78311B701B59382051 :10028000241B6B24 00 A0 :1002900 05E :1002A00 04E :1002B0003E :1002C00000 1A000E1CDF500CDE900ED7B2201AFC92A741640 :1000B00029P3A731616005F19E511C0016B :1000C000ED52D13E34D2CB00EB3E1432210129292E :1000D00029221F01C9212101F33A0D00CB :1000E000322401E6CAB6D3F2C93A2401E6FED3F2BD :1000F000320D00FBC9018000EDB0C9772310FCCDA3 :10010000D9000610C5212501CDF500C110F611E07A :1001100037211C02010600EDB0CDE900C3A70000A5 :1001200000100000001B711B5938201B451B5D246B :100l1300024ED7322013100032100004648C5E5CDBE :10014000D900E1C104567042C5E5CDE900E1C17EA8 :10015000B9F5C5E5CDD90 p; ; Accessing the Magnolia Microsystems 128K RAM Board: ; ; The MMS 128K RAM board doubles as the upper 16K of a standard 64K memory ; system and as two 56K blocks of RAM accessible through block switching. The ; 16K section is accessed normally, with no initialization necessary, but ; ; -----> A KEY-LOCK SEQUENCE IS REQUIRED TO ACCESS THE OTHER 112K. <----- ; -----> THE KEY-LOCK SEQUENCE IS OUTPUT BY MAGNOLIA'S CP/M, SO IF <----- ; -----> YOU USE MMS CP/M, DO NOT BOTHER WITH THE KEY-  | (used for | ; | | | | | | | maintenance) | ; 8K- + + +---+---+---+ + + + +-------+ + ; | | | | | R | | | | | | | ; | | | 2 | 1 | O | 0 | | | | ROM | | ; | | | | | M | | | | | | | ; 0K- +-------+-------+---+---+---+---+-------+-------+ +-------+-------+ ; | F | E | D F ; ; | | ; | - MPM Map: 16K common block - | | ; | | - CPM Map: 8K common block - | ; | | | | ; 64K- +-------+-------+---+-------+-------+ ; | | | | | | ; | | | | 0 | 0 | ; | | | | | | ; 56K- + 0 + 0 + +-------+-------+ ; | | | 4 | | | ; | | | Hso. ; If a program does not know whether the Key-Lock sequence has been ; output, it may safely output the sequence if it is in the top 8K of RAM ; (locations 0E000H to 0FFFFH) when it does so. Repeatedly outputting the ; sequence will have no effect if the outputting is done by a program running ; in these locations. ; ; Key-Lock Sequence: ; The Key-Lock sequence to be output to port 0F2H is: ; FIRST, DISABLE INTERRUPTS!!!! ; 00000100B Note: Leave the 2 millisecond clock off (bit 1 = 0) wh LOCK SEQUENCE. <----- ; ; A specific sequence of bytes (the Key-Lock sequence) must be output to ; port 0F2H before the block switching feature is activated (before any outputs ; to this port will affect the memory configuration). The first time this ; sequence is output, no block switching will take place, but the block ; switching feature will be activated (further outputs to this port will change ; the memory configuration). ONCE THE SEQUENCE HAS BEEN OUTPUT, REPEATING THE ; SEQUENCE WILL j | C | A | B | G | H | | Maintenance | ; ; ; Port 0F2H Output Bit Patterns (bits are numbered from 0 to 7): ; ('Byte' is if all other bits of the port are 0. For example, ; the 2 millisecond clock enable bit is bit 1, and map G withl ; the 2 millisecond clock on would be a byte of 15H.) ; ; Bits ; Map D5 D4 D2 Byte Notes ; --W ; A 0 0 0 00H (reset) ; B 1 0 0 20H (Std. CP/M) ; C 0 0 | | | ; | | | | | | ; 48K- +-------+-------+ 0 + + + +-------+-------+ ; | | | | | | | | ; | | | | | | | | ; ~ ~ ~ ~ ~ ~ ~ CPU board ~ ; 2 1 1 2 RAM chips ; ~ ~ ~ ~ ~ ~ ~ only ~ ; | | | | | | ile ; 00001100B this sequence is being output, and disable all interrupts. ; 00000100B Interrupts vector through locations 0000H to 0038H, and those ; 00001000B locations may not be valid when the interrupts occur if the ; 00001100B memory is not in a standard configuration. ; 00001000B ; 00100010B <---- Final byte sets up the system memory as 64K of RAM, and ; turns on the 2 millisecond clock. ; Interrupts may be re-enabled after the sequence has been output. PAGE ; ; ; Memory Maps Available: CHANGE THE MEMORY CONFIGURATION AS IT IS BEING OUTPUT, thus ; possibly switching the block holding the program outputting the sequence, ; with the result that something else (Code? A data table? 'INVISIBLE DISK' ; files?) is executed. The Key-Lock sequence may be output ONCE after the ; system is powered-up or reset without this occurring, and this is done by ; Magnolia Microsystems CP/M, so you don't have to bother if you are running ; MMS CP/M. If you are running Zenith CP/M, you will have to do X < 1 04H Use to initialize page 0 of maps E, G. ; D 1 0 1 24H Use to initialize page 0 of maps F, H. ; E 0 1 0 10H ; F 1 1 0 30H ; G 0 1 1 14H ; H 1 1 1 34H PAGE ; ; ; **"* ** MACRO ASSEMBLER DIRECTIVES **I* TITLE 'BANKSW - bank switching routines' MACLIB Z80 $-MACRO *** ** ** NOTE THIS CODE US :T; THE INTERRUPT VECTORS DB 00000100B ; IN PAGE 0 WILL PROBABLY NOT BE THERE DB 00001000B ; IF AN INTERRUPT OCCURS. DB 00001100B DB 00001000B DB 00100010B ; SET 64K OF RAM, TURN ON 2 MSEC CLOCK. LOCK$LEN EQU $-LOCK$SEQ ; LENGTH OF SEQUENCE ; ; GET 128 BYTES OF DATA FROM EXTENDED MEMORY ; READ$MEM: SSPD OLDSP ; SAVE CALLERS STACK LXI SP,STACK ; AND GET A NEW ONE CALL SETMEM ; MAP IN APPROPRIATE BANK LHLD DTALOC ; GET 128 BYTES FROM LXI D,MYBUF ; EXTENDED MEMORY PUSH D CALL MO 9 THE MAP PROM. THE BANK LETTERS BANKG EQU 00010100B ; MATCH THE MAP DESCRIPTIONS BANKH EQU 00110100B ; IN THE DOCUMENTATION. *** PAGE ORG MBASE ; START OF MODULE BEGIN: SHLD YOURBUF ; USERS BUFFER PASSED IN H,L SDED DTALOC ; FINAL DATA LOCATION IN D,E STA DSKBNK ; REQUESTED BANK PASSED IN 'A' ; CALL YOUR FUNCTION RET ; ; OUTPUT THE KEY-LOCK SEQUENCE TO ENABLE BLOCK SWITC=HING ; AND END WITH THE 64K MEMORY CONFIGURATION. ; -----> Z}THOUGH THIS IS NOT A PROGRAM WHICH WILL RUN, IT DOES ** GIVE EXAMPLES OF FUNCTIONS WHICH NEED TO BE PERFORMED TO ** ACCESS THE EXTENDED MEMORY. THESE ROUTINES OR ONES VERY ** SIMILAR ARE USED IN THE INVISIBLE DISK DRIVER. YOU MAY ** WANT TO EXAMINE T(HAT CODE ALSO BEFORE WRITING YOUR OWN. *** PAGE *** ** PORTS AND CONSTANTS *** GP EQU 0F2H ; GP P 'ES Z80 INSTRUCTIONS AND MUST BE ** ASSEMBLED WITH DIGITAL RESEARCH'S MAC(tm) ** MACRO ASSEMBLER. ** ** CP/M IS A TRADEMARK OF DIGITAL RESEARCH ** ** IF YOU ARE INTENDING TO RE-MAP MEMORY YOU NEED TO ** ** GUARANTEE ** THAT ALL DATA AND PROGRAM LOCATIONS WHICH ** WILL BE USED ARE IN THE TOP 8K OR 16K OF MEMORY, ** BECAUSE AS THE MEMORY IS BEING REMAPPED THE PROGRAM DOING ** THE REMAPPING MAY DISAPPEAR, AS THE MEMORY WHICH CONTAINS ** THE PROGRAM IS SWITCHED OUT. THIS MAY BE DONE BY BUILDING ** A KVMEM ; MOVE DATA INTO INTERNAL BUFFER CALL RESMEM ; RESTORE MEMORY CONFIGURATION POP H LDED YOURBUF ; GET THE DESTINATION ADDRESS CALL MOVMEM ; AND PUT THE DATA THERE LSPD OLDSP ; RESTORE STACK POINTER RET ; ; PUT 128 BYTES OF DATA INTO EXTENDED MEMORY ; WRITE$MEM: SSPD OLDSP ; SAVE CALLERS STACK LXI SP,STACK ; AND GET A NEW ONE LHLD YOURBUF ; GET 128 BYTES FROM CALLERS BUFFER LXI D,MYBUF ; AND INSTALL IT IN THE INTERNAL PUSH D ; BUFFER CALL MOVMEM CALL SETMEM ; MAP THIS ROUTINE MUST RUN IN THE TOP 8K OF MEMORY! <----- ; KEY$LOCK: LXI H,LOCK$SEQ ; POINT TO THE TABLE OF DATA TO BE OUTPUT MVI B,LOCK$LEN ; LENGTH OF THE TABLE MVI C,GP ; PORT TO OUTPUT THE SEQUENCE TO DI ; SHUT OFF INTERRUPTS OTIR ; Z-80 BLOCK OUTPUT INSTRUCTION EI ; INTERRUPTS BACK ON (YOU MAY NOT WANT THIS) RET ; DONE. ; ; TABLE OF DATA THAT UNLOCKS THE BLOCK SWITCHING FEATURE ; LOCK$SEQ: DB 00000100B ; DURING SEQUENCE, 2 MSEC CLOCK IS OFF. DB 00001100B ; THIS IS IMPORTAN ZORT BIT$MASK EQU 11001010B ; MEMORY MAP BIT MASK INT$BYTE EQU 00DH ; COMMON STORAGE **)* ** MEMORY BANK CONSTANTS FOR MAPPING *** BANKA EQU 00B ; THESE BANK CONSTANTS ARE BANKB EQU 00100000B ; TO BE ORed WITH THE CONTENTS BANKC EQU 00000100B ; OF INT$BYTE AND THEN OUTPUT BANKD EQU 00100100B ; TO THE 'GP' PORT. THESE ARE BANKE EQU 00010000B ; THE APPROPRIATE MAP BITS FOR BANKF EQU 00110000B ;  CP/M SYSTEM SMALLER THAN 64K AND USING THE EXTRA SPACE ** ABOVE CP/M FOR YOUR PROGRAM. THE PROGRAM WOULD NEED ** TO RELOCATE ITSELF INTO UPPER MEMORY BEFORE EXECUTION. ** ** BEFORE EACH MAP-REMAP OPERATION, THE CALLERS STACK ** SHOULD BE REPLACED. IT SHOULD BE RESTORED WHEN THE ** OPERATION IS COMPLETE. ** ** NOTE THE USE OF AN INTERNAL DATA BUFFER. THIS IS BECAUSE ** ALL DATA MUST BE MOVED INTO THE BUFFER BEFORE ANY MAPPING ** OPERATION IS PERFORMED WHICH WOULD MAKE IT INACCESSIBLE. ** ** AL IN APPROPRIATE BANK LDED DTALOC ; AND MOVE DATA TO THE POP H ; REQUESTED LOCATION. CALL MOVMEM CALL RESMEM ; RESTORE MEMORY CONFIGURATION LSPD OLDSP ; RESTORE STACK POINTER RET PAGE ; THIS ROUTINE ACTUALLY SELECTS THE DESIRED MEMORY BANK. ; NOTE HOW THE EXISTING CONTENTS OF THE INT$BYTE ARE ; SAVED. YOU MUST BE CAREFUL NOT TO CHANGE ANYTHING ; EXCEPT THE MAP BITS. THE EXCEPTION IS BIT 0 WHICH IS ; THE SINGLE STEP BIT. THIS ** MUST ** ALWAYS BE ZERO ; OR MEMORY REFRESH MAY BE D ring write, "d:filename.typ" may be destroyed.$ Link Completed$ Too Many Modules Installed$ Module is already Linked$ Linking "d:filename.typ" module into "d:filename.typ"$ !õp,!õ@^ ~#=:A|g}o )}l&#>: /^=?:F2͚27͚͚og"͚:F͚:=2Ž͚:ʎ7*#"͸G͸G:2xڿ*:=p7>!2~#"*}|*   LOCATION FOR WRITTEN DATA DSKBNK: DW 0 ; BANK OF MEMORY TO USE GPDATA: DW 0 ; EXISTING INT$BYTE YOURBUF: DW 0 ; LOCATION OF CALLERS BUFFER ; ; INTERNAL DATA BUFFER ; MYBUF: DS 128 DS 20 STACK: DS 0 ; AND STACK END  DW 0 ; LOCATION OERRUPTS RET ; ; THIS ROUTINE MOVES 128 BYTES OF DATA BETWEEN BUFFERS ; MOVMEM: LXI B,128 LDIR ; Z80 BLOCK MOVE INSTRUCTION RET PAGE ; DATA STORAGE AND BUFFERS ; OLDSP: DW 0 ; CALLERS STACK POINTER FOR RETURN DTALOC: DW 0 ; NEW ISABLED. ; ; NOTE INTERRUPTS BEING DISABLED. ON EVERY TICK OF THE ; SYSTEM CLOCK, THE CONTENTS OF INT$BYTE ARE OUTPUT TO ; PORT 'F2'. WE MUST BE CAREFUL TO GUARANTEE THE RIGHT ; BANK OF MEMORY IS MAPPED IN WHEN THIS FUNCTION IS ; PERFORMED. ; SETMEM: LXI H,DSKBNK DI ; DISABLE INTERRUPTS LDA INT$BYTE ; OLD GP PORT INFO STA GPDATA ; SAVE FOR RESTORATION LATER ANI BIT$MASK ; REMOVE MEMORY MAP BITS ; AND SINGLE STEP ORA M ; NEW MAP BITS OUT GP RET ; ; THIS IS THE COMPLEMENT OF = lO|"}y  7l$! ! [!  l ^* ~ʏ#ÐqlO|}Lʨ~ &~####°+q#lO|S9[R1, :] ʲ!\p :m ,!m- :u *:l2,2<ppʽ!,L!<LLʽ!<,! L!"n!p!,z ! * "* "* "* "! !"2FO|l M051282DRM 2 ͠ ͻ ͵ MOVCPM COM! $$$    HEX LINK version 2.240 (c) 1982 Magnolia Microsystems$ This program uses the following command format: LINK param1 {param2} param1 Name of module to link, assumed type of ".HEX" {param2} (optional) Name of MOVCPM type file, default is "MOVCPM.COM"$ O "d:filename.typ" not found$ "d:filename.typ" is unrecognizable.$ Error du   THE 'SETMEM' ROUTINE IN THAT ; IT RESTORES THE CONFIGURATION TO WHAT IT WAS WHEN ; 'SETMEM' WAS CALLED. ; RESMEM: LDA GPDATA ; OLD GP PORT INFO ANI 0FEH ; REMOVE SINGLE STEP OUT GP STA INT$BYTE ; TO CLEAN UP INFORMATION EI ; AND ENABLE INTERRUPTS RET ; ; THIS ROUTINE MOVES 128 BYTES OF DATA BETWEEN BUFFERS ; MOVMEM: LXI B,128 LDIR ; Z80 BLOCK MOVE INSTRUCTION RET PAGE ; DATA STORAGE AND BUFFERS ; OLDSP: DW 0 ; CALLERS STACK POINTER FOR RETURN DTALOC: DW 0 ; NEW JGC! "lOlGCxlO|}& <~qlO|9* yl+O|YS* "* K Cx{*[*ySl+Ҭ|L* +! :W#x ~w#}#^*+~! K[yXy &w#~S~JSgnutS:w#w*]T[ "*  K Cxv* [" * [" ! ! L/ !,\L!"}|:% w#"(  x:* $2K !I B:* $2R !P B*# DM*, q#p͊=ɯ20 *# DM*, ~r#~~͊Y:0 A͇>20 ͇>20 G*. xGҜ4œ5# ‘*8 >(Ү+"8 >25 !7 ? k+k!; B:* $2Y [ *. ~k# !W B> !D B:+ 0-(a{ 00. 7~#-Be@(a xRL>-~̀~̀ 0#z(Rʟe:* 12> !^B! " " " " " " " " " " "* ", !a B2+ 0<2+ 0<2+ 02+ !(c) Magnolia Microsystems 198212!^B2+ +0<2+ 0<2+ 02+ 25 ! " " " " !"3 o"# !a B+!% BN:6 }*3 |ʟ! BLNÖ!W B!"6 ͣ!   !D  !a B! Bͻ6*6 #"6 *6 },0[:+ <:5 <25 2+ ͝6:5 &! B!a B*# #"# ͫ͠Ͷͻ*6 #"6 *# #"# :6 œ!: ("3 !4  k+k!7  k+k!a B͏>!r >!j  >!b @>!Z `>!R >!J >!B >!: 2* ". "ţo>g"mL4 K Cx{*[*ySl+Ҭ|L* +! :W#x ~w#}#^*+~! K[yXy &w#~S~JSgnutS:w#w*]T[ "*  K Cxv* [" * [" ! ! L/ !,\L!"}| .25 xxGyO!]~O͊!~O͊-y2! :]!!  k!] k!m BLWcEy4TEST128K 2.4 - Magnolia Microsystems' z89 Extended-RAM Memory Test Block X Bit Errors Detected in the Bank Pass: Bank Address Range 7 6 5 4 3 2 1 0 in <==> out 0 0000-1FFF 8k 1 2000-3FFF 2 4000-5FFF 3 6000-7FFF 32k 4 8000-9FFF 5 A000-BFFF 48k 6 C000-DFFF 7 BE000-FFFF 64k Enter an R at any time to restart the test. If o, 2#Je͏!6 Dw#!Ʌo>g!6 D:+ QKT Tz !7 :+ v*  Å  Å  k+k!9 k+k:+ K[>4!K[>! K[> !z 6 D*B *R >25 !z BY%v+! B!^B2* j/<!: o|g". :* !9!2% "& "( *& V#"& *( zW \~any errors occur, see the dealer from whom you purchased the board.Test program relocated here. Y0 Error summary:K Top 8k: errors in passes. K Block 0: errors in passes.K Block 1: errors in passes.K Block 2: errors in passes.K Y"^Y"&Y\<=Y\=>Y7 Y5 J BANK SWITCH ERROR -- Continue test anyway (Y/N)?Y5 JG Functional test OK. Shift/Reset to end test, or leave running for mor ** DO NOT execute the file "RAMDISKMM.COM" without reading and understanding the directions in the file "RAMDISK.DOC". RAMDISKMM.COM was developed for use by people who are using the Heath/Zenith release of CP/M. Users of the MMS release of CP/M should follow the printed instructions in the manual. ath/Zenith release of CP/M. Users of the MMS release ofts the use of the "RAMDISK.COM" program which can be used to add INVISIBLE DISK features to your Zenith CP/M system. % $$H A$I!!$H$@I$$H$DI!$$I$$HAHD! BA$I$DI$HI$I$$ $ "$@H$A "D$ I H$B$ @D$B@I$I$$H$@$HI$H any time to reset the test. Kpe thorough testing.Y5 J Y6 J MEMORY ERRORS IN BANK 7 (E000-FFFF), BLOCK 0 BLOCKING RELOCATION. REBOOT!Y5 J MEMORX PARALYZED DUE TO CHECKSUM ERROR - BANK 7 (E000-FFFF). Sum: Checksum: Y5 J Please remove disk(s) from drive(s), then press to initiate the RAM test - Y5 J Testing Bank 7, Block 0 before relocating.Y5 J Bank 7 is OK. Relocating, testing other blocks.Y5 J ERROR IN BANK 7 -- TESTING BANK 7 FOREVER. REBOOT.Y, K KExtended memory testing begun. Enter an R at MDISK. OPERATION - In order to operate, RAMDISK must be told two things: what it's logical drive address (B:-P:) is to be, and whether or not it's directory space is to be cleared. These parameters can be given to RAMDISK in two ways: AUTO MODE - In AUTO mode, the parameters are passed on the command line as follows: A>RAMDSKMM drive: initialization where drive: is the logical drive assignment, B:-P:, and initialization is either a 'Y', o`THIS FILE SUPPLEMENTS THE PRINTED DOCUMENTATION INCLUDED WITH YOUR RAM BOARD. PLEASE READ IT BEFORE CALLING WITH ANY QUESTIONS. ** The MMS utility "LINK.COM" has been included on this distribution disk, in case it is not present on your original MMS CP/M distribution disk. ** Users of the Heath/Zenith release of CP/M should read the file "RAMDISK.DOC", as it documents the use of the "RAMDISKMM.COM" program which can be used to add INVISIBLE DISK features to your Zenith CP/M system.  any time to reset the test. K-esting other blocks.Y5 J ERROR IN BANK 7 -- TESTING BANK 7 FOREVER. REBOOT.Y, K KExtended memory testing begun. Enter an R at any time to reset the test. Kp 3r 'N' to indicate whether the directory is to be cleared of ALL files. Some examples: A>RAMDSKMM M: Y Would cause the RAMDISK to be assigned as drive M:, and its directory to be cleared. A>RAMDSKMM D: N Would cause the RAMDISK to be assigned as drive D:, and its directory would NOT be cleared, leaving any existing files intact. The "drive:" assigment can be anything from B: to P:. RAMDISK CANNOTn now re-run RAMDISK at any time to alter the drive assignment, or force a directory initializaion. Re-assigning RAMDISK to another logical drive does not affect any data in RAMDISK. hon. The responses are exactly the same as above. PROMPT mode will also be entered anytime AUTO mode is used, and an invalid parameter is given on the command line. Note that, unlike the previous version of RAMDISK, you ca be assigned to drive A:, since it CANNOT be SYSGENED, and therefore CANNOT be booted from. Note that when you tell RAMDISK to clear the directory, ALL information in RAMDISK will be lost, so be VERY careful when exercising this option!! The directory clearing is required since on power-up the contents of the RAM is not known, and the directory space could contain information which CP/M may inter- pret as valid directory entries, even though they are in fact gar bage. Also note that AUTO mode allows RAMDISK to be loaded by a SUBMIT or auto-run command, with no input required by the user. PROMPT MODE - In PROMPT mode, RAMDISK will prompt the user for the drive assignment and initialization. The responses are exactly the same as above. PROMPT mode will also be entered anytime AUTO mode is used, and an invalid parameter is given on the command line. Note that, unlike the previous version of RAMDISK, you ca   !            {    i    _    M             !$!!!!!! !!! """"""" """ #6###### ### $$$$$$$ $$$ %%%%%%% %%% &&&&&&& &&& ''''''' ''''