nippur72 Posted January 17, 2019 Share Posted January 17, 2019 I've extended the emulator to work with double sided disks :-) Yes the tracks are reversed on the second side, I guess it makes sense to minimize head movements and also for backward compatibility.I've also added some debug features for the disk drive so you can see exactly how it works. From the JavaScript console modify the variables: fdc_debug_move = true; // debugs movements of the drive head fdc_debug_data_size = true; // debugs changes in 8/16 bit flag fdc_debug_read = true; // debugs byte reads fdc_debug_write = true; // debugs byte writes fdc_debug_side = true; // debugs changes in disk side (0/1)The self-sync bit flag seems to be either 255 (on) or 213 (off) but there are less ONs that OFFs, suggesting that the controller switches automatically to ON somewhere. Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted January 18, 2019 Share Posted January 18, 2019 Hi Nippur72, I've extended the emulator to work with double sided disks :-) Yes the tracks are reversed on the second side, I guess it makes sense to minimize head movements and also for backward compatibility. The self-sync bit flag seems to be either 255 (on) or 213 (off) but there are less ONs that OFFs, suggesting that the controller switches automatically to ON somewhere. Nice work on the double-sided code. Works fine here for me! Looking at the read/write code, I think that whenever the *WRREQ bit (bit 6 in port $10) is turned ON, the controller defaults to writing in self-sync mode. Writing $FF (255) to port $11 turns self-sync *ON*. Writing *any other* value to port $11 turns self-sync *OFF*. I think the 213 ($D5) value is just incidental, as the A register happens to be holding $D5 (next byte written after the self-sync sequence). --- It's interesting to note that the self-sync flag is turned off *after* the $D5 byte is actually written to the data output register. That suggests the self-sync flag is not sampled by the FDC prior to the byte being output, but can be changed mid-byte. I'm presuming the $D5 byte is *not* written as a 10 bit byte? I don't have the real hardware to test unfortunately. (It doesn't actually matter either way if the $D5 ID byte is written as 8 or 10 bits, it will still just magically work ) RAM:66BF D3 10 out (10h), a ; turn on *WRREQ - self-sync *on* by default. RAM:66C1 7A ld a, d ; D = $FF here RAM:66C2 D3 13 out (13h), a RAM:66C4 loc_66C4: RAM:66C4 CD 11 79 call WRITE_DISK_BYTE_D RAM:66C7 10 FB djnz loc_66C4 ; Write $FF 127 times on first loop, sector_gap on rest RAM:66C9 16 D5 ld d, 0D5h RAM:66CB CD 11 79 call WRITE_DISK_BYTE_D RAM:66CE D3 11 out (11h), a ; self-sync *off* RAM:66D0 16 AA ld d, 0AAh RAM:66D2 CD 11 79 call WRITE_DISK_BYTE_D RAM:66D5 16 96 ld d, 96h --- Here is the current state of my disassembly of DOS1.1: http://crisis.com.au/images/LASER_DOS_1.1_DISS.zip Includes the Boot Sector at $A200 Main DOS from $5E00-$7FFF (loaded into bank#6, executed in slot#1) High memory routines copied to $FEE0-$FFAD I've concentrated more on the read/write routines than on the high-level BASIC stuff, but it should be reasonably easy to follow. Regards, Leslie Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted February 4, 2019 Share Posted February 4, 2019 Hi James, et al. The BASIC with the 500 appears to be pretty good but the way the first letter of the tokens are stored seems unique. At least I haven't seen anything like it before. Maybe some sort of logic or math operation with it's position in the table? The tokeniser routine at $2509 in the ROM as it processes the input line, uses the first ascii character it finds as an index into a 26 word pointer table, for A-Z: RAM:2509 TOKENIZE: ; CODE XREF: sub_2457+7D↑j RAM:2509 ; sub_2457+93↑j RAM:2509 E1 pop hl ; convert basic keyword to 1 byte TOKEN RAM:250A CD 15 31 call sub_3115 RAM:250D E5 push hl RAM:250E 21 E9 1B ld hl, KEYWD_PTR_ARRAY ; 26 char table, first letter of each RAM:2511 D6 41 sub 41h ; 'A' ; A now ZERO OFFSET into table RAM:2513 87 add a, a RAM:2514 4F ld c, a RAM:2515 06 00 ld b, 0 RAM:2517 09 add hl, bc RAM:2518 5E ld e, (hl) RAM:2519 23 inc hl RAM:251A 56 ld d, (hl) ; DE now points at inividual letter table RAM:251B E1 pop hl RAM:251C 23 inc hl .... RAM:1BE9 1D 1C KEYWD_PTR_ARRAY:dw KEYWORDS_A ; DATA XREF: sub_2457+B7↓o RAM:1BEB 2E 1C dw KEYWORDS_B RAM:1BED 2F 1C dw KEYWORDS_C RAM:1BEF 83 1C dw KEYWORDS_D RAM:1BF1 B0 1C dw KEYWORDS_E RAM:1BF3 D1 1C dw KEYWORDS_F RAM:1BF5 E7 1C dw KEYWORDS_G RAM:1BF7 FB 1C dw KEYWORDS_H RAM:1BF9 00 1D dw KEYWORDS_I RAM:1BFB 1C 1D dw KEYWORDS_J RAM:1BFD 20 1D dw KEYWORDS_K RAM:1BFF 28 1D dw KEYWORDS_L RAM:1C01 5C 1D dw KEYWORDS_M RAM:1C03 81 1D dw KEYWORDS_N RAM:1C05 94 1D dw KEYWORDS_O RAM:1C07 AA 1D dw KEYWORDS_P RAM:1C09 C8 1D dw KEYWORDS_Q RAM:1C0B C9 1D dw KEYWORDS_R RAM:1C0D 0A 1E dw KEYWORDS_S RAM:1C0F 47 1E dw KEYWORDS_T RAM:1C11 62 1E dw KEYWORDS_U RAM:1C13 6B 1E dw KEYWORDS_V RAM:1C15 7B 1E dw KEYWORDS_W RAM:1C17 93 1E dw KEYWORDS_X RAM:1C19 97 1E dw KEYWORDS_Y RAM:1C1B 98 1E dw KEYWORDS_Z RAM:1C1D 55 KEYWORDS_A: db 'U' ; DATA XREF: RAM:KEYWD_PTR_ARRAY↑o RAM:1C1E 54 db 'T' RAM:1C1F CF db 0CFh RAM:1C20 AB db 0ABh ; AUTO $AB RAM:1C21 4E db 'N' RAM:1C22 C4 db 0C4h RAM:1C23 F7 db 0F7h ; AND $F7 RAM:1C24 42 db 'B' RAM:1C25 D3 db 0D3h RAM:1C26 06 db 6 ; ABS $06 RAM:1C27 54 db 'T' RAM:1C28 CE db 0CEh RAM:1C29 0E db 0Eh ; ATN $0E RAM:1C2A 53 db 'S' RAM:1C2B C3 db 0C3h RAM:1C2C 15 db 15h ; ASC $15 RAM:1C2D 00 db 0 RAM:1C2E 00 KEYWORDS_B: db 0 ; DATA XREF: RAM:1BEB↑o RAM:1C2F 4C KEYWORDS_C: db 'L' ; DATA XREF: RAM:1BED↑o RAM:1C30 4F db 'O' .... and so on. Following program will list all the keywords and their corresponding single-byte tokens: 10 A=&H1C1D 20 C=&H40 30 C=C+1 40 B=PEEK(A) 50 IF B=0 THEN A=A+1:GOTO 30 60 PRINT CHR$©; 70 IF B > 127 THEN 110 80 PRINT CHR$(B); 90 A=A+1 100 B=PEEK(A):GOTO 70 110 B=(B AND 127) 120 PRINT CHR$(B), 130 A=A+1 140 B=PEEK(A) 150 PRINT" Token:$";HEX$(B) 160 A=A+1 170 IF A < &H1E96 THEN 40 Still working on discovering all the entry points for the BASIC commands and keywords. Regards, Leslie Quote Link to comment Share on other sites More sharing options...
nippur72 Posted February 9, 2019 Share Posted February 9, 2019 Still working on discovering all the entry points for the BASIC commands and keywords. do you already know about "basck" ? it's tool in the Z88DK compiler that automatically scans a ROM dump and finds MS basic entry points: # File size: 32768 # Specific Z80 CPU code detected # Microsoft 8080/Z80 BASIC found # Extended syntax detected (classic version) # Double precision maths detected # Microsoft signature not found BASTXT = $8041 ; BASIC program start ptr (aka TXTTAB) # CPDEHL (compare DE and HL), code found at $5FA1 # (Detected position for ORG: 0) INPORT = $8579 ; Current port for 'INP' function OTPORT = $857C ; Current port for 'OUT' statement SEED = $8586 ; Seed for RND numbers LSTRND2 = $8580 ; Last RND number BUFFER = $8149 ; Start of input buffer TMSTPT = $839F ; Temporary string pool pointer TMPSTR = $83BF ; Temporary string NXTOPR = $83E3 ; Address ptr to next operator PROGND = $83E9 ; BASIC program end ptr (aka VARTAB) VAREND = $83EB ; End of variables ARREND = $83ED ; End of arrays (lowest free mem) SGNRES = $852F ; Sign of result TEMPST = $83A1 ; (word), temporary descriptors TEMPPT = $839F ; (word), start of free area of temporary descriptor PRMLEN = $840D ; (word), number of bytes of obj table NOFUNS = $84DE ; (byte), 0 if no function active PRMLN2 = $8475 ; (word), size of parameter block FUNACT = $84E1 ; (word), active functions counter PRMSTK = $840B ; (word), previous block definition on stack SUBFLG = $83CC ; (byte), flag for USR fn. array TEMP = $83CE ; (word) temp. reservation for st.code VALTYP = $838E ; (word) type indicator PRITAB = $1EAE ; (word) Arithmetic precedence table TEMP2 = $83E3 ; (word) temp. storage used by EVAL TEMP3 = $83C4 ; (word) used for garbage collection or by USR function SAVTXT = $83D6 ; (word), prg pointer for resume TEMPST = $83A1 ; (word), temporary descriptors TEMPPT = $839F ; (word), start of free area of temporary descriptor CURLIN = $803F ; (word), line number being interpreted OLDLIN = $83E5 ; (word), old line number set up ^C ... SAVTXT = $83D6 ; (word), prg pointer for resume OLDTXT = $83E7 ; (word), prg pointer for CONT FPREG = $852B ; Floating Point Register (FACCU, FACLOW on Ext. BASIC) FPEXP = $852E ; Floating Point Exponent DBL_FPREG = $852B ; Double Precision Floating Point Register (aka FACLOW) DBL_LAST_FPREG = $853A ; Last byte in Double Precision FP register (+sign bit) CPDEHL = $5FA2 ; compare DE and HL FNDNUM = $3507 ; Load 'A' with the next number in BASIC program GETINT = $350A ; Get a number to 'A' DEPINT = $34EF ; Get integer variable to DE, error if negative FPSINT = $34E9 ; Get subscript POSINT = $34EC ; Get positive integer GETVAR = $59B7 ; Get variable address to DE DIM = $59B2 ; DIM command CHKSTK = $5E9E ; Check for C levels of stack OPRND = $304E ; Get next expression value SYNCHR = $5FA8 ; Check syntax, 1 byte follows to be compared LFRGNM = $655B ; number in program listing and check for ending ')' HLPASS = $3DFA ; Get back from function passing an INT value HL MIDNUM = $6560 ; Get number in program listing INT_RESULT_A = $3C6F ; Get back from function, result in A (signed) GETYPR = $320F ; Test number FAC type (Precision mode, etc..) TSTSGN = $3C35 ; Test sign of FPREG _TSTSGN = $3C76 ; Test sign in number INVSGN = $3C64 ; Invert number sign STAKFP = $3C88 ; Put FP value on stack NEGAFT = $49F9 ; Negate number LOG = $3AC0 ; LOG EXP = $4A63 ; EXP TAN = $4C4C ; TAN ATN = $4C61 ; ATN DBL_ABS = $3FC6 ; ABS (double precision BASIC variant) RND = $4B14 ; RND SUBCDE = $3999 ; Subtract BCDE from FP reg FPADD = $399C ; Add BCDE to FP reg SCALE = $3A6B ; Scale number in BCDE for A exponent (bits) PLUCDE = $3A4B ; Add number pointed by HL to CDE COMPL = $3A57 ; Convert a negative number to positive PHLTFP = $3C95 ; Number at HL to BCDE FPBCDE = $3C98 ; Move BCDE to FPREG MLSP10 = $3C1E ; Multiply number in FPREG by 10 FPMULT = $3B05 ; Multiply BCDE to FP reg DIV10 = $3B5D ; Divide FP by 10 DIV = $3B66 ; Divide FP by number on stack DCBCDE = $3E48 ; Decrement FP value in BCDE BCDEFP = $3CA3 ; Load FP reg to BCDE LOADFP = $3CA6 ; Load FP value pointed by HL to BCDE CMPNUM = $3CF1 ; Compare FP reg to BCDE FPINT = $3E24 ; Floating Point to Integer FLGREL = $3C44 ; CY and A to FP, & normalise INT = $3E6E ; INT DBL_SUB = $3FEC ; Double precision SUB (formerly SUBCDE) DBL_ADD = $3FF3 ; Double precision ADD (formerly FPADD) FIX = $3E4F ; Double Precision to Integer conversion INT_MUL = $3F29 ; Integer MULTIPLY MLDEBC = $3EE1 ; Multiply DE by BC _ASCTFP = $42A0 ; ASCII to FP number H_ASCTFP = $429B ; ASCII to FP number (also '&' prefixes) PRNUMS = $62D2 ; Print number string PRS = $62D3 ; Create string entry and print it PRS1 = $62D6 ; Print string at HL STR = $624C ; STR BASIC function entry SAVSTR = $6255 ; Save string in string area MKTMST = $626F ; Make temporary string CRTMST = $6272 ; Create temporary string entry SSTSA = $6437 ; Move string on stack to string area TOSTRA = $643F ; Move string in BC, (len in L) to string area TSALP = $6440 ; TOSTRA loop FDTLP = $2EA7 ; Find next DATA statement DATA = $29EA ; DATA statement: find next DATA program line.. RESTOR = $5FBA ; 'RESTORE' stmt, init ptr to DATA program line.. NEW_STMT = $27B5 ; Interprete next statement GO_TO = $2993 ; Go To.. MAKINT = $350D ; Convert tmp string to int in A register CONCAT = $63FF ; String concatenation TESTR = $62EB ; Test if enough room for string TOPOOL = $649E ; Save in string pool TSTOPL = $62AD ; Temporary string to pool EVAL = $2ED4 ; (a.k.a. GETNUM, evaluate expression (GETNUM) EVAL1 = $2ED7 ; Save precedence and eval until precedence break EVAL3 = $2EE9 ; Evaluate expression until precedence break CRTST = $627D ; Create String QTSTR = $627E ; Create quote terminated String DTSTR = $6281 ; Create String, termination char in D GETSTR = $6448 ; Get string pointed by FPREG 'Type Error' if it is not GSTRCU = $644B ; Get string pointed by FPREG GSTRHL = $644E ; Get string pointed by HL GSTRDE = $644F ; Get string pointed by DE TSTSTR = $3E1D ; Test a string, 'Type Error' if it is not LNUM_RANGE = $241A ; Read numeric range function parameters LNUM_PARM = $2921 ; Read numeric function parameter _CHRGTB = $281A ; Pick next char from program _CHRCKB = $281B ; Pick current char (or token) on program UCASE_HL = $3115 ; Get char from (HL) and make upper case UCASE = $3116 ; Make char in 'A' upper case RINPUT = $0C1C ; Line input OUTC = $57D9 ; Output char in 'A' to console DATSNR = $2222 ; 'SN err' entry for Input STMT SNERR = $2228 ; entry for '?SN ERROR' ULERR = $29CA ; entry for '?UL ERROR' # JP table for statements = $1AD1 # TOKEN table position = $1C1D, word list in 'extended BASIC' mode. # Token range: 88 # -- STATEMENTS -- # USING [229] - $5CD1 # TAB( [220] - $2C53 # SPC( [224] - same as TAB( # = assignment [240] # + operand [242] - $304E # - operand [243] - $30F7 # " string [34] - $627E # NOT [225] - $31FA # & specifier [38] - $3124 # ERR [227] - $3083 # ERL [226] - $3093 # VARPTR [232] - $30A3 # USR [234] - $12B6 # INSTR [222] - $327C # TOKEN_? [230] - $6565 # TOKEN_? [233] - $5964 # TOKEN_? [228] - $64A2 # TOKEN_? [133] - $5310 # TOKEN_? [223] - $32F1 # ELSE [162] # AUTO [171] - $2B15 # AND [247] # ABS [6] - $3C57 # ATN [14] - $4C61 # ASC [21] - $6484 # CLOSE [195] - $859A # CONT [154] - $602D # CLEAR [146] - $60D8 # CINT [28] - $3D61 # CSNG [29] - $3DD7 # CDBL [30] - $3E03 # CVI [43] - $5099 # CVS [44] - $509C # CVD [45] - $509F # COS [12] - $4BA9 # CHR$ [22] - $6494 # CALL [182] - $5410 # COMMON [184] - $8585 # CHAIN [185] - $8588 # CSAVE [155] - $1593 # CLOAD [156] - $1714 # CLS [160] - $0F14 # COLOR [167] - $0F19 # CRUN [207] - $1958 # COPY [210] - $1AC7 # DELETE [170] - $36FD # DATA [132] - $29EA # DIM [134] - $59B2 # DEFSTR [173] - $28D0 # DEFINT [174] - $28D3 # DEFSNG [175] - $28D6 # DEFDBL [176] - $28D9 # DEF [152] - $32CA # DRAW [212] - $12BE # ELSE [162] - $29EC # END [129] - $5FD8 # ERASE [166] - $6091 # ERROR [168] - $2B0A # ERL [226] # ERR [227] # EXP [11] - $4A66 # EOF [47] - $85BB # EQV [250] # FOR [130] - $26D3 # FIELD [192] - $8591 # FILES [198] - $85A3 # FN [223] # FRE [15] - $6682 # FIX [31] - $3E4F # GOTO [137] - $2994 # GO TO [137] - $2994 # GOSUB [141] - $297D # GET [193] - $8594 # GR [190] - $10B5 # HEX$ [26] - $6247 # INPUT [133] - $2D4A # IF [139] - $2B47 # INSTR [230] # INT [5] - $3E62 # INP [16] - $3489 # IMP [251] # INKEY$ [233] # JOY [32] - $1A7E # KILL [200] - $85A9 # KEY [205] - $0FE2 # LPRINT [158] - $2B85 # LLIST [159] - $3519 # LPOS [27] - $326D # LET [136] - $2A11 # LINE [177] - $2CDE # LOAD [196] - $859D # LSET [201] - $85AC # LIST [147] - $351E # LOG [10] - $3AC0 # LOC [48] - $85BE # LEN [18] - $6478 # LEFT$ [1] - $64E4 # LOF [49] - $85C1 # MERGE [197] - $85A0 # MOD [252] # MKI$ [50] - $5080 # MKS$ [51] - $5083 # MKD$ [52] - $5086 # MID$ [3] - $651D # MOTOR [209] - $0F57 # MOVE [211] - $12BB # MON [214] - $6B96 # NEXT [131] - $615C # NULL [150] - $6041 # NAME [199] - $85A6 # NEW [148] - $5EF9 # NOT [225] # OPEN [191] - $858E # OUT [157] - $3495 # ON [149] - $2A77 # OR [248] # OCT$ [25] - $6242 # OPTION [186] - $38A8 # PRINT [145] - $2B8D # PUT [194] - $8597 # POKE [153] - $373F # POS [17] - $3272 # PEEK [23] - $3735 # POINT [234] # PAINT [213] - $85B8 # RETURN [142] - $29CF # READ [135] - $2E1A # RUN [138] - $2966 # RESTORE [140] - $5FBA # REM [143] - $29EC # RESUME [169] - $2AC0 # RSET [202] - $85AF # RIGHT$ [2] - $6514 # RND [8] - $4B14 # RENUM [172] - $3778 # RESET [204] - $85B5 # RANDOMIZE [187] - $38F2 # RES [179] - $12C1 # STOP [144] - $5FD4 # SWAP [165] - $6050 # SAVE [203] - $85B2 # SPC( [224] # STEP [221] # SGN [4] - $3C6C # SQR [7] - $49FE # SIN [9] - $4BAF # STR$ [19] - $624C # STRING$ [228] # SPACE$ [24] - $64CB # SYSTEM [189] - $858B # SET [178] - $12C4 # SOUND [206] - $1198 # THEN [219] # TRON [163] - $604A # TROFF [164] - $604B # TAB( [220] # TO [218] # TAN [13] - $4C4C # TEXT [188] - $0F8A # USING [229] # USR [222] # VAL [20] - $653E # VARPTR [232] # VERIFY [208] - $1962 # WIDTH [161] - $34BC # WAIT [151] - $349C # WHILE [180] - $5380 # WEND [181] - $53A1 # WRITE [183] - $54D3 # XOR [249] # Z # [+ [242] # [- [243] # [* [244] # [/ [245] # [^ [246] # [\ [253] # [' [231] # [> [239] # [= [240] # [< [241] # Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted February 12, 2019 Share Posted February 12, 2019 Hi Nippur72, et al. do you already know about "basck" ? it's tool in the Z88DK compiler that automatically scans a ROM dump and finds MS basic entry points: I was aware of it, but hadn't though to try it. Your list shows it does a pretty good job of locating most things. ---- Can I trouble you to test a couple of things on your real machine? I'm looking at Bits #6 and #7 of &H6800 on write. Bit#7 should be the cassette motor control output. Bit#6 is turned on/off by a routine which hangs off the interrupt chain if a certain key combination is pressed: SHIFT-CONTROL-1 (I think) I'm wondering if Bit#6 controls the "GT" output of the gate array, which is connected to the character generator? If so, it may switch visible character sets on machines which have CGA11 and CGA12 correctly jumpered? Cheers, Leslie RAM:0A56 sub_A56: ; CODE XREF: RAM:0A1A↑p RAM:0A56 RAM:0A56 ; FUNCTION CHUNK AT RAM:0AD3 SIZE 0000000B BYTES RAM:0A56 RAM:0A56 06 02 ld b, 2 ; Bring in I/O Bank RAM:0A58 CD E5 0A call SET_BANK1_B ; set Bank#1 to value in B reg, save old on stack, and return RAM:0A5B 21 FF 6A ld hl, 6AFFh ; Keyboard row 'C'? RAM:0A5E CB 76 bit 6, (hl) ; check CAPS LOCK ? RAM:0A60 28 11 jr z, loc_A73 RAM:0A62 CD A3 0A call CHECK_CONTROL_KEY ; check if control key is down - ZERO if true RAM:0A65 20 2F jr nz, loc_A96 RAM:0A67 21 F7 6F ld hl, 6FF7h ; check Row KA3 RAM:0A6A CB 6E bit 5, (hl) ; bit 5 = '1' key? RAM:0A6C 20 28 jr nz, loc_A96 RAM:0A6E 3E 3F ld a, 3Fh ; '?' RAM:0A70 32 F0 85 ld (byte_85F0), a RAM:0A73 RAM:0A73 loc_A73: ; CODE XREF: sub_A56+A↑j RAM:0A73 21 FB 85 ld hl, operation_flags2 RAM:0A76 CB 6E bit 5, (hl) RAM:0A78 20 21 jr nz, loc_A9B RAM:0A7A CB EE set 5, (hl) RAM:0A7C 3A F9 85 ld a, (REG_6800W) ; Saved copy of $6800 I/O Write byte RAM:0A7F CB 77 bit 6, a RAM:0A81 20 0D jr nz, loc_A90 RAM:0A83 CB F7 set 6, a ; what does Bit #6 in $6800 do in write mode RAM:0A85 CB 9E res 3, (hl) RAM:0A87 RAM:0A87 loc_A87: ; CODE XREF: sub_A56+3E↓j RAM:0A87 32 00 68 ld (loc_67FF+1), a RAM:0A8A 32 F9 85 ld (REG_6800W), a ; Saved copy of $6800 I/O Write byte RAM:0A8D C3 D3 0A jp RESTORE_BNK1 ; Restore Bank#1 from values on the stack RAM:0A90 ; --------------------------------------------------------------------------- RAM:0A90 RAM:0A90 loc_A90: ; CODE XREF: sub_A56+2B↑j RAM:0A90 CB B7 res 6, a RAM:0A92 CB DE set 3, (hl) RAM:0A94 18 F1 jr loc_A87 Quote Link to comment Share on other sites More sharing options...
nippur72 Posted February 16, 2019 Share Posted February 16, 2019 I tried to flip bit 6 and bit 7 of &H6800 (with bank 1 switched to 2), but nothing happens. I tried that in 40 and 80 columns. Nothing.I also tried CTRL+SHIFT+key on all keys, but it behaved normally. MON 9000Z DI LD A, 02 OUT (41), A LD A, C0 LD (6800), A LD A, 01 OUT (41), A EI RET If you have any other test you want me to run, just ask. Quote Link to comment Share on other sites More sharing options...
nippur72 Posted February 17, 2019 Share Posted February 17, 2019 Leslie, I think you've found something not satisfied by yesterday's test, I debugged the unidentified bits in the emulator looking for how and when they were changed. Bit 7 and 4 still remain untouched everywhere; but bit 6 it's the CAPS LOCK state (CAPLK in the schematics). CAPLK is also triggered by CTRL+1 as you've found in your disasselmby. The reason? Laser 350 does not have a dedicated caps lock key, so they made it available via this key combination. Somewhere in the keyboard routine there also should be the code that checks the actual caps lock key and flips the bit. This is the code I used for debbuging in the emulator, you can try it yourself: open the JavaScript console (F12) and paste this: (function() { let old_io_bit_7, old_caps_lock_bit, old_io_bit_4; debugBefore = ()=> { old_io_bit_7 = io_bit_7; old_caps_lock_bit = caps_lock_bit; old_io_bit_4 = io_bit_4; } debugAfter = ()=> { let { pc } = cpu.getState(); if(old_io_bit_7 != io_bit_7) console.log(`bit 7 changed from ${old_io_bit_7 } to ${io_bit_7 } at ${hex(pc,4)}`); if(old_caps_lock_bit != caps_lock_bit) console.log(`bit 6 changed from ${old_caps_lock_bit} to ${caps_lock_bit} at ${hex(pc,4)}`); if(old_io_bit_4 != io_bit_4) console.log(`bit 4 changed from ${old_io_bit_4 } to ${io_bit_4 } at ${hex(pc,4)}`); } })(); Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted February 18, 2019 Share Posted February 18, 2019 Hi Nippur72, Leslie, I think you've found something not satisfied by yesterday's test, I debugged the unidentified bits in the emulator looking for how and when they were changed. Bit 7 and 4 still remain untouched everywhere; but bit 6 it's the CAPS LOCK state (CAPLK in the schematics). Thanks for the debug code! That explains the code for the CTRL-1 == capslock! ----- I tried your debug code out with the "MOTOR ON" and "MOTOR OFF" commands from BASIC. They are definitely flipping bit#7 in &H6800 writes. "MOTOR ON" gives: bit 7 changed from 0 to 1 at 0f7c "MOTOR OFF" gives: bit 7 changed from 1 to 0 at 0f7c If you have a cassette recorder attached with the correct remote cable attached and the play and/or record buttons down, it should start and stop the recorder. Cheers, Leslie Quote Link to comment Share on other sites More sharing options...
nippur72 Posted February 18, 2019 Share Posted February 18, 2019 Ahah great! I wasn't even aware there was a "MOTOR" command! Nice finding as well!Since there is no dedicated "cassette motor" plug, I guess it's not connected. Perhaps it's the REMOTE (70) pin on the VLSI chip (not connected in the 350/500/700 schematics) Quote Link to comment Share on other sites More sharing options...
Platis Posted February 19, 2019 Share Posted February 19, 2019 Maybe little off topic, but have you ever seen this book before? https://www.ebay.com/itm/Programming-the-Dick-Smith-Electronics-VZ300-1986-/333038434946?hash=item4d8aa33a82 Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted February 23, 2019 Share Posted February 23, 2019 Hi Platis, Maybe little off topic, but have you ever seen this book before? https://www.ebay.com/itm/Programming-the-Dick-Smith-Electronics-VZ300-1986-/333038434946?hash=item4d8aa33a82 Can't say that I have seen that one. Doesn't appear to be on any of the popular VZ200-300/LASER 310 repositories that I can see. No use for programming the Laser 350/500/700 models of course. Cheers, Leslie Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 26, 2019 Share Posted April 26, 2019 Hi, I need some help... I'm going to make a FPGA implementation of the Laser 500 and need to understand better the matter about the clock. Or I should say clocks, because looking at the schematics there are two of them. The first is 17.73447 Mhz which should be the standard PAL reference for generating the colour burst signal.The other is a 14.77873 Mhz, which divided by 4 should provide the 3.6947 Mhz CPU clock.I have included the schematic in the picture below. Both clocks go directly into the big VLSI chip (F17M and F14M pins). From my little understanding F14M is not the actual 14 MHz signal, but it is divided by 4 via the flip flops.What I don't understand is:1) why there are two clocks at all? Knowing that at VTECH they were cost-reducing oriented, why didn't they derive the CPU clock by dividing the video by 5? (resulting in a 3.546894 Mhz signal).2) how they cope with the fact that the pixel-clock (for generating the video) and CPU clock are not multiples and thus not aligned? (e.g. when accessing RAM).3) looking at the 74LS273 in the schematic (the one with the 7 flip-flops) I don't get how the divide by 4 is achieved... shouldn't it be just two flip flops?4) most important question: why on earth the CPU clock divider takes as input the HSYNC signal?? From what I know, HSYNC is a signal that is always "1" except for a brief period at the start of every scanline. Doesn't this mean that the CPU stops for a little at every scan line? Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted April 27, 2019 Share Posted April 27, 2019 Hi Nippur72, et al. 1. Not sure. 2. CPU Clock and Mem accesses are synchronised to the F14M signal. (pg 214 of the tech manual, posted in this thread) 3. The flip-flops provide a digital delay, not a divide by 4 . Pin 9 of the LS02 takes a product of the delay circuit. When pin 9 is low, F14M runs freely same as T14M, but inverted. When pin 9 is high, F14M is held low. When HSYNC goes from High to low, the circuit holds F14M low for 6 (or 7) 14Mhz clocks. When HSYNC goes from low to high, there is no delay. 4. Maybe the GA1 uses this time to reload the colour registers etc. in preparation for the next display line? Cheers, Leslie Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 27, 2019 Share Posted April 27, 2019 Leslie, thanks for the detailed explanation!It's still obscure why the CPU has to be put on hold during HSYNC, as there should be plenty of time to load registers for the new line (the beam is still in the invisible area of the CRT). My guess is that it's a fix to problem that arose after the production of the big VLSI chip, otherwise the delay line and the other gates would have been included directly into the chip, right?So there are 7 clock cycles skipped on every scanline, for a total of 7 x 312 = 2184 cycles. This means a 2184 : 4 = 546 Hz slowdown for the CPU. Unfortunately that is only a small part of the difference between the nominal CPU frequency (3.6947 Mhz) and what I have deduced experimentally from my tests (~3.6702 Mhz). There is a ~25000 Hz difference lost somewhere else within the chip (that number is after taking into account the wait states for memory access).It might be that the missing cycles are for something that is scaline-based, like the above HSYNC. If this is the case, it would be ~25000 Hz / 312 lines = ~80 cycles lost at each scanline. And since the screen/graphic is 80-column based, it might be that the CPU is put on hold for 1 cycle at every grid column (for loading the bitmap etc...).What do you think? Is that reasonable? If so, I could finally fix my emulator! Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted April 28, 2019 Share Posted April 28, 2019 Hi Nippur72, It's still obscure why the CPU has to be put on hold during HSYNC, as there should be plenty of time to load registers for the new line (the beam is still in the invisible area of the CRT). My guess is that it's a fix to problem that arose after the production of the big VLSI chip, otherwise the delay line and the other gates would have been included directly into the chip, right? So there are 7 clock cycles skipped on every scanline, for a total of 7 x 312 = 2184 cycles. This means a 2184 : 4 = 546 Hz slowdown for the CPU. Unfortunately that is only a small part of the difference between the nominal CPU frequency (3.6947 Mhz) and what I have deduced experimentally from my tests (~3.6702 Mhz). There is a ~25000 Hz difference lost somewhere else within the chip (that number is after taking into account the wait states for memory access).It might be that the missing cycles are for something that is scaline-based, like the above HSYNC. If this is the case, it would be ~25000 Hz / 312 lines = ~80 cycles lost at each scanline. And since the screen/graphic is 80-column based, it might be that the CPU is put on hold for 1 cycle at every grid column (for loading the bitmap etc...).What do you think? Is that reasonable? If so, I could finally fix my emulator! I thought about the problem some more.. It's possible that stopping the F14M clock in that position has something to do with the colour burst signal generation. Your suggestion that it may be an add-on circuit to fix a bug in the VLSI chip that was only discovered after they were produced may not be a bad guess either. I don't think that the missing cycles have anything to do with scanline dependent features. According to the timing diagram, there is a one cpu clock wait state inserted for *every* memory access, whether it be read/write/opcode fetch. Are you accounting for the instructions and overhead during the interrupt after each vertical retrace? Regards, Leslie P.S. I think it is an interesting co-incidence that the Laser 350/500/700 series employs the same control register address ($6800) as the earlier LASER 310/VZ200/VZ300 machines. The graphics/text screen layouts on the 350/500/700 are basically the same as an Apple ][. It would come as no surprise to learn that VTECH used some of the same VLSI code from their LASER 3000 series machine here. I had a quick look at the Laser 3000 tech manual, but it does not have a delay line circuit like the 350-700. Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 28, 2019 Share Posted April 28, 2019 I am already accounting for the memory access done by the CPU; I count +1 for every memory/io accesses made by the last instruction (my emulator works at instruction level). This gets very close but still faster than it should be.I am not good at reading the clock diagram, does it mean that the wait state is also insert when the VDC accesses the memory, not just the CPU? If so, I am not accounting that, because I don't know how and when the VDC reads the memory (but ok, we can guess it).If this is the case, it would be 80 bytes read on every line (border excluded). I think no matter what graphic mode is on, they are always 80 bytes (e.g. in TEXT 40: 40 characters + 40 colors, etc).so: 3694700 - 192*80 - 312*(7/4) (for HSYNC) = 3678794This is still a bit faster, it should be something around 3672000.Could it be that the HYSC wait is triggered for the whole HSYNC period, not just on the positive edge of it? For numbers to match it needs to be around 22 cycles of F14M. Regarding the copy from Apple, eh eh yes VTech was not new to it :-) The whole FD-100 disk drive is a clone, and also they had the Laser 128 which was a whole computer cloned! Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted April 29, 2019 Share Posted April 29, 2019 (edited) I am already accounting for the memory access done by the CPU; I count +1 for every memory/io accesses made by the last instruction (my emulator works at instruction level). This gets very close but still faster than it should be. I am not good at reading the clock diagram, does it mean that the wait state is also insert when the VDC accesses the memory, not just the CPU? If so, I am not accounting that, because I don't know how and when the VDC reads the memory (but ok, we can guess it). If this is the case, it would be 80 bytes read on every line (border excluded). I think no matter what graphic mode is on, they are always 80 bytes (e.g. in TEXT 40: 40 characters + 40 colors, etc). so: 3694700 - 192*80 - 312*(7/4) (for HSYNC) = 3678794 This is still a bit faster, it should be something around 3672000. Could it be that the HYSC wait is triggered for the whole HSYNC period, not just on the positive edge of it? For numbers to match it needs to be around 22 cycles of F14M. Regarding the copy from Apple, eh eh yes VTech was not new to it :-) The whole FD-100 disk drive is a clone, and also they had the Laser 128 which was a whole computer cloned! According to the timing diagram, the CPU and VIDEO sub-section share access to the RAM memory on each alternate CPU clock cycle. A 1:1 basis. Third line of the diagram shows the CPU CK (CPU CLOCK). CPU doesn't know anything about the video section, so it can issue a memory read/write instruction fetch at *any* time, during the CPU "slot" or VIDEO "slot". You will need to take a look at a Z80 tech manual to see that each memory read/write operation takes 3 clock cycles ("T" states) and an instruction fetch takes 4. (Assuming no wait states). On a mem read/write cycle, *MREQ , *RD both go low on the falling edge of the T1 state. *WR goes low on the falling edge of the T2 state. Referring back to the Laser timing diagram, this will always be when CPU CK is LOW. There is no way of knowing if that falls in the CPU allocated slot, or the video slot. VIDEO always reads a byte in one "slot" CPU always requires two "slots". Consider for a minute that the CPU clock is 3.6947 Mhz. That means that the period of the CPU clock is somewhere close to 271ns. Plenty of time for the VIDEO section to read a byte from RAM in it's "slot". The point where the CPU asserts *RD or *WR in the cycle (3/4 of the way through the "slot") doesn't allow enough time for the RAMS to respond however. A one-cpu-clock wait state is therefore always inserted for every memory byte read, memory byte write and for each byte of the instruction fetch (including one wait state for each operand byte) regardless of which "slot" the CPU read/write is initiated in. I don't think that I/O instructions have a wait state inserted according to the diagram. --- Regarding the HSYNC. No, it's only triggered on the falling edge... according the the diagram. Cheers, Leslie Edited April 29, 2019 by RedskullDC Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 29, 2019 Share Posted April 29, 2019 Leslie, thanks for the explanation! Got it, but still it seems we don't have all the information needed to find out the missing CPU cycles...Perhaps it's better to focus on the VDC timings first, if we get it right then we can use the raster or some other trick to deduce the other timings.I assume the pixel clock is F14M (14778730) as any sub-multiple of it isn't able of displaying 640 pixel horizontally. I also assume the VDC renders 312 scanlines as it is commonly done (not certain but it's in line with some of my tests). Another assumption is that there are no clock cycles spent between two frames (this is also common).I have measured a refresh rate of ~49.7 Hz using the interrupt, this gives me the following formula:14778730 / 312 / 49.7 = 953.07 pixels per line. When rounded, 953 gives back a refresh rate of 49.7038 Hz.The value of 953 pixels includes HSYNC, front and back porch, left and right borders and active area. All of these are unknown, except the active area which is of course 640 pixels. The unknown values be guessed by making them close to a standard PAL signal:HSYNC = 4.7us = 70 pixelsblank = 5.65us = 84 pixelsvisible area = 52us = 780 pixels (70 + 640 + 70)front porch = 1.65us = 21 pixelsA way these numbers can be tested is to generate a PAL signal out of it and see if the active area size matches the one of a real Laser 500. It could be the first step in my FPGA implementation.Any other idea? Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted April 30, 2019 Share Posted April 30, 2019 They look like sane values to produce a PAL signal. I don't think there is any way to figure out the missing CPU signals without putting a logic analyser on a real machine. To assist with that , I just purchased a Laser 500 which I saw on Ebay: https://www.ebay.com.au/itm/202660668891 -- I wouldn't be too concerned with emulating the PAL output and memory wait states in an FPGA version. It really isn't necessary Much easier to use dual port memory for the video memory regions, and let the video and cpu run completely independently. If the machine is running a bit fast, can always lower the CPU clock. Disk system runs independently off its' own 4MHz clock, so no need to sync that. 17MHz signal is not required at all on the FPGA. If you want to use the entire screen, recommend using 1280x960 as the video output standard (4:3) On the max resolution screen (640 x 192) each horizontal pixel is displayed twice, and each vertical line is displayed 5 times. 640 x 2 = 1280, 192 * 5 = 960. Otherwise, SVGA=800x600. Multiply the vertical lines by 3 = 576. Centre the video, and display the border register colour when outside the 640x192 area. Cheers, Leslie Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 30, 2019 Share Posted April 30, 2019 good about the Laser 500 purchase, they are quite rare nowdays! Does it have the manual too? (BTW, I forgot to tell that a friend is already scanning the english one, we'll have it in the coming weeks).As for the FPGA implementation, the fun is to implement the machine as close as possible to the original, that's my target :-)My board (the "MiST") has a composite output so it's born to produce a PAL or NTSC signal directly, which is cool if you still own an old CRT TV/Monitor. Another Laser 500 owner is going to measure the composite signal with an oscilloscope. It will be interesting to know how many lines are actually rendered (if they are not 312 then all the calculations are incorrect).For a VGA output, 800x600 is a good suggestion because it's able to show a little of the borders, but I'm not sure I can generate a standard 800x600 VGA signal out of the F14M signal (or multiples). In case I'll use a frame buffer and make it independent as you suggested. 1 Quote Link to comment Share on other sites More sharing options...
nippur72 Posted April 30, 2019 Share Posted April 30, 2019 I'm hacking my JavaScript emulator to make it simulate an FPGA, it's a lot easier to catch bugs than working on the FPGA directly. The CPU has increased drastically but still under 100%. It's incredible how JavaScript can emulate a chip running at 14 MHz ! Here is the simulated video signal that it's generating... notice the HSYNC, front and back porch and the VSYNC. The active area is still messy. Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted May 1, 2019 Share Posted May 1, 2019 (edited) good about the Laser 500 purchase, they are quite rare nowdays! Does it have the manual too? (BTW, I forgot to tell that a friend is already scanning the english one, we'll have it in the coming weeks). As for the FPGA implementation, the fun is to implement the machine as close as possible to the original, that's my target :-) My board (the "MiST") has a composite output so it's born to produce a PAL or NTSC signal directly, which is cool if you still own an old CRT TV/Monitor. Another Laser 500 owner is going to measure the composite signal with an oscilloscope. It will be interesting to know how many lines are actually rendered (if they are not 312 then all the calculations are incorrect). I don't think the one I purchased came with any manuals The auction pics show it to be a QWERTY model keyboard, with only ENGLISH keytops. Will be interesting to see if the ROMS are the same as what we already have. Definitely looking forward to a scanned copy of the BASIC manual and tech manual. They would clear up a lot of mysteries I'm sure! ---- Easiest way to implement the wait states on the 500 is to just have a circuit which inserts a one-cpu-clock wait state for every memory read/write/instruction fetch. There isn't any need to tie it to the video generation to achieve the same effect as a real machine. Better to have the video run independently, then you can have any output frequency you like. Doesn't need to be tied to the 14.7MHz clock. The digital delay line circuit introduces a ~ 473ns (1000ns/ 14.7MHz * 7 = 473ns) pause in the 14Mhz clock after every *HSYNC. If you are planning to use the same Z80 core as the LASER_310_FPGA project, it already has a clock enable input which you could use to mimic this delay. Re-creating the hardware as close as possible is a great idea. Don't think I'm trying to point you in a different direction. At some point (if you are like me) you will most likely want to extend the FPGA implementation to : a) run faster b) emulate disks/cassettes c) more memory d) extended screenmodes (such as the Laser 3000 HI-RES RGB modes) With that in mind, it is best not to cripple your video output section to mimic hardware kludges from the 1980's if you don't have to Look forward to seeing your progress! Cheers, Leslie Edited May 1, 2019 by RedskullDC Quote Link to comment Share on other sites More sharing options...
Platis Posted May 1, 2019 Share Posted May 1, 2019 "Laser Computer Club" magazine; https://archive.org/search.php?query=laser%20computer%20club Quote Link to comment Share on other sites More sharing options...
nippur72 Posted May 1, 2019 Share Posted May 1, 2019 The ENGLISH keytops is also the ones I have (two of them). Messages back in this thread we found the ROM contains the ENG,GER,FRA charset which are selected by a switch on the board, so it's likely that the ROM is the same. Regarding the HSYNC delay, there is one thing that is it not clear to me... the circuit delays F14M which feeds the VLSI chip that in turn generates the HSYNC signal. So the chip puts itself on hold? Is that correct? I already have a working PAL signal that matches quite closely the original. And I am also coding the video generation, I wrote it first in a Verilog-like JavaScript and it's mostly working. 1 Quote Link to comment Share on other sites More sharing options...
RedskullDC Posted May 2, 2019 Share Posted May 2, 2019 Regarding the HSYNC delay, there is one thing that is it not clear to me... the circuit delays F14M which feeds the VLSI chip that in turn generates the HSYNC signal. So the chip puts itself on hold? Is that correct? That is correct, but it also gives us the answer to the reason for delay circuit! Since the F14M clock is stopped, nothing is advancing in the VLSI. The *only* effect that the delay circuit has is to stretch the HSYNC pulse by around 470ns Presumably the HSYNC pulse coming out of the VLSI chip is too short due to an error in the logic which was only discovered after the VLSI chips were already manufactured? Has the unfortunate side effect of slowing the CPU down by 470ns every horizontal line. Cheers, Leslie Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.