Page 2 of 2

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Thu Aug 25, 2011 7:42 am
by Andy Rea
Ah, that will explain it then. the effect of moving the entire creen up 1 scanline was by accident rather than design. The way my interrupt handler works at the top of the screen the nmi routine jumps to my code before generating the vsync, i then jump to the normal rom routine to genrate Vsync and read the keyboard but becuase the NMI routie has already pushed the main registers on the stack i have to find a way to control where thr rom routine returns too. so before jumping to the rom routine i manipulate the stack by putting IY (so that i can run code that uses IY) and a return address UNDER the 4 main registers on the stack, remeber the rom routine pops these main registers before returning.
so why does it move the display, because on a real zx81 Vsync does not reset/hold the hsync generator it continues to run just like it always does, only 2 things cause the Hsync to reset, a roll over from count 207 to 0 and an int ack (M1 low, and IORQ low) the upshot is that because of the extra time spent manipulating the stack we get an extra hsync that the character generator line counter counts after the vsync has ended. so on a real zeddy to counter this i decrease the number of blank lines in the top margin by 1 hence the entire display moves up by 1 line, i also increase the number of blank lines in the lower margin by 1 so overall display generation frequency remains the same.


i'll post the code later, im on the wrong PC at the moment.

Andy

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Thu Aug 25, 2011 6:29 pm
by Andy Rea

Code: Select all

;===================================================
;=                                                 =
;= ZX81 interrupt handler, By Andrew Rea.          =
;=                                                 =
;= due to the complex way the ZX81 generates its   =
;= display during 'SLOW' mode if you wish to       =
;= carry out a regular task at every interrupt     =
;= you must manipulate the IX register which is    =
;= used as a jump vector within the ZX81 rom.      =
;=                                                 =
;= But to keep a steady display you must also      =
;= either copy the rom code to generate the display=
;= or write your own code.                         =
;=                                                 =
;= this solution uses mostly the original rom.     =
;=                                                 =
;= it also saves and restores the IY register,     =
;= which is use in the rom code, normally you      =
;= should not run USER code that uses the IY       =
;= but since it is saved things seem to work ok    =
;=                                                 =
;===================================================

;writtne for tasm assembler, with undocumented instructions
;supported.

;
; Compile with "tasm -80 -b input_source_code.asm output_binary_file.bin
;
;

 
#define db .byte ; TASM cross-assembler definitions
#define DB .byte
#define dw .word
#define DW .word
#define ds .block
#define DS .block
#define org .org
#define ORG .org
#define end .end
#define END .end


;=========================
;=                       =
;= you can change the    =
;= following to suit the =
;= address of the PT3    =
;= player module.        =
;=                       =
;=========================
PLAYER_START .EQU $2000
PLAYER_PLAY  .EQU $2002
PLAYER_MUTE  .EQU $2005

;this will be compiled as a ZX81 .p file

 
	org	$4009
 
;= System variables ============================================
 
	db 0			;VERSN
	dw 0     		;E_PPC
	dw dfile      	;D_FILE
	dw dfile+1    	;DF_CC
	dw var   		;VARS
	dw 0     		;DEST
	dw var+1      	;E_LINE
	dw last-1     	;CH_ADD
	dw 0     		;X_PTR
	dw last  		;STKBOT
	dw last  		;STKEND
	db 0     		;BERG
	dw membot     	;MEM
	db 0     		;not used
	db 2     		;DF_SZ
	dw 1     		;S_TOP
	db $FF,$FF,$FF   ;LAST_K
	db 55    		;MARGIN
	dw dfile      	;NXTLIN
	dw 0     		;OLDPPC
	db 0     		;FLAGX
	dw 0     		;STRLEN
	dw $0C8D      	;T_ADDR
	dw 0     		;SEED
	dw $FFFF      	;FRAMES
	db 0,0   		;COORDS
	db $BC   		;PR_CC
	db 33,24      	;S_POSN
	db %01000000  	;CDFLAG
	ds 33    		;Print buffer
membot:
	ds 30    		;CalculatorĀ“s memory area
	ds 2     		;not used
 
;= First BASIC line, asm code ==================================
 
line0:
	
	db 0,0
	dw dfile-$-2
	db $ea   		;zx81 token REM

;= Your code should follow this file ========================== 
;=
;= This is the intterupt handler
	
CONTROL:
	
	DB	$00				;used as a way of getting a signal into the interrupt handler
						;
						;on initialise ' call start '
						;
						; CONTROL = 0
						;
						; 0~	DON'T CALL PLAYER DURRING INT
						;	BUT SILENCE SOUND.
						; 
						; 1 ~ CALL PLAYER DURRING INT
						;
						; 255 ~ SILENCE SOUND AND
						;		STOP INTTERUPT HANDLER
PT3FILE_ADDRESS:

	DW	$8000			;DEFAULT ADDRESS FOR PT3 FILE, ALTER THIS
						;TO THE ADDRESS OF YOUR PT3 FILE
						;THEN START THE HANDLER
	
START:

	XOR	A				;ZERO
	LD	(CONTROL),A		;STORE IN CONTROL BYTE
						;AFTER INITIALISING
						;PLAYER WILL NOT PLAY
						;UNTIL USER ALTERS THIS BYTE
						;TO = 1

GO_HANDLER:

TEST_X:	
;LD	A,IXL				;margin is still in top
;	DB	$DD,%01111101
;	CP	$81
;	JR	NZ,TEST_X			;top of screen
;LD	A,IXL
TEST_X_2:	
	DB	$DD,%01111101		;test for bottom
	CP	$8F
	JR	NZ,TEST_X_2		;
						
						;AND THEN LOAD IX REGISTER SO OUR
	LD	IX,END_OF_BOTTOM_MARGIN	
						;HANDLER IS USED INSTEAD
	
INIT_PLAYER:	

	LD	HL,(PT3FILE_ADDRESS)
	CALL	PLAYER_START		;INITIALISE THE PLAYER

	RET					;BACK TO USERS PROGRAM
	
;=============================
;=                           =
;= DISPLAY INTTERUPT HANDLER = 
;=                           =
;==================================================
;=                                                =
;= The display intturupt handler has 2 main parts =
;=                                                =
;= during user program execution the NMI int are  =
;= processed by incrementing the a' register (from=
;= the af' pair ) and when zero is reached the    =
;= main registers AF, BC, DE, HL are pushed onto  =
;= the stack and a jump is made to address held   =
;= in IX. The nmi's are active during the 'blank' =
;= area's at the top and bottom of the diaply     =
;= these are the top and bottom margins.
;=                                                =
;= So by altering IX we can take control of the   =
;= display intterupt cycle                        =
;=                                                =
;= 1) create a vsync signal and read keyboard.    =
;= 1a) start top margin, and resume user program. =
;=                                                =
;= 2) generate the display.                       =
;= 2a) start bottom margin, and resume user prog- =
;=                                           gram =
;=                                                =
;==================================================


;NOW THE TRICKY BIT !
;
;==========
;=        =
;= PART 1 =
;=        =
;==========

END_OF_BOTTOM_MARGIN:

;NMI ROUTINE JUMPS HERE AT THE END OF THE BOTTOM MARGIN.
;THIS REPLACES THE $028F JUMP.

;when we get here the NMI routine has already put AF, BC, DE, HL on the stack
;HL on last
;
; sp --->	HL
;		DE
;		BC
;		AF

;nmi is also turned off
;but i want it to return here so i can alter IX again.
;so beacuase the rom routine will pop these off before RET-ing
;i need to get my return address under 4 words on the stack
;

 	LD HL, AFTER_VSYNC	;the address i wish to return to 
 					;after the call to rom.


;RIGHT GONNA TRY A DIFFERENT APPROACH TO THIS.
;
;four items on stack...

	POP	AF			;HL IN AF, leaves 3
	POP	DE			;DE IN DE, leaves 2
	POP	BC			;BC IN BC, leaves 1
					;AF STILL ON STACK
	EX	(SP),IY		;swap IY and final value on stack
					;IY it could be any value by now...
	PUSH	HL			;THE RETURN ADDRESS where execution will
					;continue after ROM code has run
	PUSH	IY			;AF BACK ON STACK
	PUSH	BC			;BC BACK ON STACK
	PUSH	DE			;DE BACK ON STACK
	PUSH	AF			;HL BACK ON STACK
	
;so now we have IY on the stack then our return address and then the original 
;4 registers pushed by the NMI routine.
	
	LD	IY,$4000		;SET IY FOR ROM CODE TO RUN CORRECTLY

	JP	$0229
					;THIS ROM ROUTINE NORMALLY POPS THE REGISTERS AND
					;RETURNS TO USER CODE EXECUTION
					;WITH IX LOADED $0281, READY FOR NEXT VIDEO INT.
					;BUT NOW IT RETURNS HERE, BECAUSE I SHOVED EXTRA STUFF
					;ON THE STACK
AFTER_VSYNC:
START_OF_TOP_MARGIN:
					;so we should have returned here
					;with just IY left on the stack
	POP IY



;NOW ALL THIS PUSHING AND POPPING MEANS WE HAVE MISSED THE FIRST HSYNC
;REMEBER HSYNC ARE NOT RESET BY VSYNC, BUT A NORMAL INT ACK.
;SO NOW ALL THE CHARACTERS ARE MOVED UP 1 PIXEL LINE (LINE COUNTER IN ula)
;SO MUST DECREMENT THE NMI INTERUPT COUNTER (IT ACTUALLY COUNTS UP)
;
;Ha Ha, I've known for a while that eightyone does not correctly
;handle the hsync timming. so the character are displayed wrong on eightyone.
;
;wonder if this will work on any other emulator correctly ?
;vb81xur displays it ok, but my test for IXL going to $8f doesn't seem 
;to work on VB81xur

;originally was turning off nmi whilst adjusting the a' register
;but tried it without and it works just fine on real ZX81
;could be another place for emulators to fall over ;-)

;	out	($fd),a		;TURN OFF nmi JUST INCASE AN NMI HAPPENS WITH THE
	ex af,af'			;THE WRONG AF PAIR ACTIVE
	inc	a			;DECREMENT THE LINE COUNTER (YES IT COUNTS UP)
	ex af,af'			;SWAP BACK TO NORMAL AF
;	out	($fe),a		;AND TURN ON THE nmi AGAIN
	
RESET_IX_VECT:			;FINALLY WE ARE READY TO RETURN TO USER CODE
	LD	IX,END_OF_TOP_MARGIN	;SET THE IX REG READY FOR NEXT END OF TOP MARGIN
	
	RET						;RETURN TO USER PROGRAM
	
END_OF_TOP_MARGIN:




;==========
;=        =
;= PART 2 =
;=        =
;==========

;NMI ROUTINE HAS NOW JUMPED HERE AT THE END OF THE TOP MARGIN
;NMI'S ARE OFF
;STACK IS AS BEFORE

;MUST PRETTY MUCH COPY ROM ELSE TOP FEW LINES OF DISPLAY GET CORRUPTED
	
	LD	A,R				; SAME AS
	LD	BC,$1901			; ROM
	LD	A,$F5			; CODE
	CALL	$02B5			; BUT....
     
;RETURNS HERE WHEN DISPLAY IS DONE
START_OF_BOTTOM_MARGIN



	PUSH	IY				;SAVE iy ONCE AGAIN
						
	LD	a,($4028)			; get the margin from sys-vars

	NEG                     	; Negate
						; normal rom increments (one less margin line)
	;INC	A               	; MISSING THIS INC COMPENSATES FOR THE ONE 
						; ONE LESS ABOVE
	EX	AF,AF'          	; place negative count of blank lines in A'
	

	LD	IX,END_OF_BOTTOM _MARGIN	;AND MAKE SURE IX IS LOADED
	
	OUT	($FE),A         	; enable the NMI generator.

; THATS PRETTY MUCH THE INTERRUPT HANDLER
; main registers are still on the stack with IY on top.

	
;LETS PLAY SOME MUSIC ;-)

; IF (CONTROL) = 255 THEN ix LOADED WITH NORMAL ROM VECTOR (STOPS HANDLER RUNNING )
;                                                          (NEXT TIME NMI ROUTINE )
;                                                          (DOES A  JP (IX) after )
;											    (after the top margin.  ) 
;	AND ALSO MUTES PLAYER.
;
; IF (CONTROL) = 0 THEN MUTES PLAYER.
;
; IF (CONTROL) = 1 THEN CALLS PLAYER EVERY 1/50TH SECOND
;
; IF (CONTROL) = 'other value' THEN DO NOTHING; SO SOUND CHIP WILL CONTINUE TO SOUND...
;                                                          LAST DATA SENT TO IT...

	LD	A,(CONTROL)
	INC	A						;TEST FOR 255
	JR	NZ,DONT_EXIT_INTS			;ONLY 255 WILL RSULT IN ZERO

EXIT_INTS:
	LD	IX,$028F					;NORMAL ix VECTOR
	INC	A						;TRICK NEXT TEST

DONT_EXIT_INTS:
	exx
	push	hl
	exx
	DEC	A						;TEST FOR ZERO
	CALL	Z,PLAYER_MUTE				;IF IT WAS 0 RESULT WILL BE ZERO
	DEC	A						;OR 1
	CALL	Z,PLAYER_PLAY					;OTHER VALUES WILL DO NOTHING
	exx
	pop	hl
	exx							;HERE
; THATS IT FOR THE PLAYER BIT.

POPS:	
	pop	IY				;restored from entry to int routine
	POP	HL              	;AND FINNALY RESTORE USERS REGISTERS 
	POP	DE              	; 
	POP	BC              	; 
	POP	AF              	; 

;AND ENDS HERE        
	
	RET                   	; BACK TO USER PROG.
	
          
	
;=====================================


		


;= your code should end here ===================================
;zx81end.asm
;used at end of file....

   db $76   		;N/L end of line 0
 
;- Display file --------------------------------------------
 
dfile:
   db $76
   db $76,$76,$76,$76,$76,$76,$76,$76
   db $76,$76,$76,$76,$76,$76,$76,$76
   db $76,$76,$76,$76,$76,$76,$76,$76
 
;- BASIC-Variables ----------------------------------------
 
var:
   db $80
 
;- End of program area ----------------------------
 
last:
 
   end
hopefully you can tell what its doing from the comments....

Andy

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Thu Aug 25, 2011 10:16 pm
by Oliver
Aaaargh. Seems my homebrew Zeddy made from standard-logic-chips manages to emulate the emulators in real hardware:

ZXTEST.P running on TTL-based Zeddy
ZXTEST.P running on TTL-based Zeddy
With_zxtest_lo.jpg (81.99 KiB) Viewed 5409 times

The challenge was to build a ZX81-compatible with as few standard-ICs as possible. I knew I omitted some shifters in the sync-Logic, but why..? It will take me a while to understand that, if ever...

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Thu Aug 25, 2011 10:37 pm
by Andy Rea
Hi Oliver,

Thats a nice home brew you got there, gow many chips is it ? do you still have the schematics for it ?

But This is what i was expecting from homebrew machines, if my theory is correct, i did notice however that the first scanline is shifted to the left also.

Andy

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Thu Aug 25, 2011 11:28 pm
by Oliver
Hi Andy,

The Zeddy-board has 14 Chips + RAM+ROM+CPU. Schematics can be found here:

http://forum.tlienhard.com/phpBB3/viewt ... =331#p1647 (sorry, mostly in German, but you will find the ZX81_Clone_Schem.zip file with the schematics).

I already know that the HSync-Timing differs, making the board run few more cycles between the NMIs. Intermediately I improved that timing ( ZX81_Clone_2_Schem.zip, still 14 TTLs) but the wiring on the board became too complicated, so I reverted to the initial version. Thats the downside of using standard-logic - compared to FPGAs debugging is somewhat harder.

Oliver

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Fri Aug 26, 2011 7:45 pm
by siggi
Bodo Wenzel also did a test with his "FPGA-Zeddy"-clone. And he has also the effect like on EO. Here are some pictures he sent to me:
Bodo-Z1.JPG
(717.42 KiB) Downloaded 1486 times
Bodo-Z2.JPG
(637.09 KiB) Downloaded 1488 times
Siggi

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Fri Aug 26, 2011 8:13 pm
by Andy Rea
Bodo can fix it....
it's a simple fix for CPLD or FPGA....
It all revolves around the Hsync counter, Vsync does NOT STOP the hsync counter NOR does it reset it... only 2 things reset Hsync counter

1) a roll around from max-count back to zero

2) and int-ack (M1 and IORQ both low).

But also the hsyncs are generated 16 cycles after the reset.

i use the following code in my CPLD ULA which as far as i can tell works exactly like the real thing (yet to be proved wrong :lol:)

Code: Select all

reg [7:0]hc;
wire hc_reset;
assign hc_reset = (!m1 & !iorq);



always @ (negedge slow_clock) begin
	if (hc_reset | hc == 206) begin
		hc <= 8'd0 ;
	end else begin
		hc <= hc + 8'd1 ;
	end
end

wire hsync;

assign hsync = !hc[7] & !hc[6] & !hc[5] & hc[4] ;
So Hsync is generated when count reaches 16 thru to 31, also fed to NMI signal if NMI's are on.

It's a bit more trouble to fix 74xx series clones but not impossible :-)

Andy

P.s That a really nice clone machine that Bodo has made :-)

Re: does your zeddy (or emulator) work like the real thing ?

Posted: Sat Aug 27, 2011 5:53 pm
by zx80nut
Yeah, I was going to do that with my NMI gen, but haven't managed to do it with a low chip count (but have got it working with more chips).
That is, reset on INTACK then 16 cycle pulse 16 cycles after the INTACK ...that's what I measured on the real ZX81 when I did the previous version.
I want to keep the chip count no higher than 6 (currently 5 chips for the VSYNC reset version). I'll keep on trying, and will post if successful :)