1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 *   Yabause - linkage_x86.s                                               *
3 *   Copyright (C) 2009-2011 Ari64                                         *
4 *                                                                         *
5 *   This program is free software; you can redistribute it and/or modify  *
6 *   it under the terms of the GNU General Public License as published by  *
7 *   the Free Software Foundation; either version 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 *                                                                         *
10 *   This program is distributed in the hope that it will be useful,       *
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13 *   GNU General Public License for more details.                          *
14 *                                                                         *
15 *   You should have received a copy of the GNU General Public License     *
16 *   along with this program; if not, write to the                         *
17 *   Free Software Foundation, Inc.,                                       *
18 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
19 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20	.file	"linkage_x86.s"
21	.bss
22	.align 4
23	.section	.rodata
24	.text
25.globl YabauseDynarecOneFrameExec
26	.type	YabauseDynarecOneFrameExec, @function
27YabauseDynarecOneFrameExec:
28	push	%ebp
29	mov	%esp,%ebp
30	mov	master_ip, %eax
31	xor	%ecx, %ecx
32	push	%edi
33	push	%esi
34	push	%ebx
35	push	%ecx /* zero */
36	push	%ecx
37	push	%ecx
38	push	%ecx
39	push	%ecx /* put m68k here (?) */
40	push	%ecx
41	call	.+5 /* 40+4=44 */
42	mov	%eax,-40(%ebp) /* overwrite return address */
43/* Stack frame:
44   arg2 - m68kcenticycles (+8/+12)
45   arg1 - m68kcycles (+4/+8)
46   return address (0)
47   ebp (4/0)
48   save edi (8/4)
49   save esi (12/8)
50   save ebx (16/12)
51   decilinecount (20/16)
52   decilinecycles (24/20)
53   sh2cycles (28/24)
54   scucycles (32/28)
55   ... (36/32)
56   ... (40/36)
57   ret address/master_ip (44/40) (alternate esp at call)
58   save %eax (48/44)
59   save %ecx (52/48)
60   save %edx (56/52)
61   ... (esp at call)
62   next return address (64/60)
63   total = 64 */
64/*   usecinc?
65   cyclesinc?*/
66
67newline:
68/* const u32 decilinecycles = yabsys.DecilineStop >> YABSYS_TIMING_BITS; */
69/* const u32 cyclesinc = yabsys.DecilineStop * 10; */
70	mov	decilinestop_p, %eax
71	mov	yabsys_timing_bits, %ecx
72	mov	(%eax), %eax
73	lea	(%eax,%eax,4), %ebx /* decilinestop*5 */
74	shr	%cl, %eax /* decilinecycles */
75	shl	%ebx	/* cyclesinc=decilinestop*10 */
76	lea	(%eax,%eax,8), %edx  /* decilinecycles*9 */
77        /* yabsys.SH2CycleFrac += cyclesinc;*/
78        /* sh2cycles = (yabsys.SH2CycleFrac >> (YABSYS_TIMING_BITS + 1)) << 1;*/
79        /* yabsys.SH2CycleFrac &= ((YABSYS_TIMING_MASK << 1) | 1);*/
80	mov	SH2CycleFrac_p, %esi
81	mov	yabsys_timing_mask, %edi
82	inc	%ecx /* yabsys_timing_bits+1 */
83	add	(%esi), %ebx /* SH2CycleFrac */
84	stc
85	adc	%edi, %edi /* ((YABSYS_TIMING_MASK << 1) | 1) */
86	mov	%eax, -20(%ebp) /* decilinecycles */
87	and	%ebx, %edi
88	mov	%edi, (%esi) /* SH2CycleFrac */
89	shr	%cl, %ebx
90	mov	%ebx, -28(%ebp) /* scucycles */
91	add	%ebx, %ebx /* sh2cycles */
92	mov	MSH2, %eax
93	mov	NumberOfInterruptsOffset, %ecx
94	sub	%edx, %ebx  /* sh2cycles(full line) - decilinecycles*9 */
95	mov	%ebx, -24(%ebp) /* sh2cycles */
96	cmp	$0, (%eax, %ecx)
97	jne	master_handle_interrupts
98	mov	master_cc, %esi
99	sub	%ebx, %esi
100	ret	/* jmp master_ip */
101	.size	YabauseDynarecOneFrameExec, .-YabauseDynarecOneFrameExec
102
103.globl master_handle_interrupts
104	.type	master_handle_interrupts, @function
105master_handle_interrupts:
106	mov	-40(%ebp), %eax /* get return address */
107	mov	%eax, master_ip
108	call	DynarecMasterHandleInterrupts
109	mov	master_ip, %eax
110	mov	master_cc, %esi
111	mov	%eax,-40(%ebp) /* overwrite return address */
112	sub	%ebx, %esi
113	ret	/* jmp master_ip */
114	.size	master_handle_interrupts, .-master_handle_interrupts
115
116.globl slave_entry
117	.type	slave_entry, @function
118slave_entry:
119	mov	16(%esp), %ebx /* sh2cycles */
120	mov	%esi, master_cc
121	sub	$12, %esp
122	push	%ebx
123	call	FRTExec
124	mov	%ebx, (%esp)
125	call	WDTExec
126	mov	slave_ip, %edx
127	add	$16, %esp
128	test	%edx, %edx
129	je	cc_interrupt_master /* slave not running */
130	mov	SSH2, %eax
131	mov	NumberOfInterruptsOffset, %ecx
132	cmp	$0, (%eax, %ecx)
133	jne	slave_handle_interrupts
134	mov	slave_cc, %esi
135	sub	%ebx, %esi
136	jmp	*%edx /* jmp *slave_ip */
137	.size	slave_entry, .-slave_entry
138
139.globl slave_handle_interrupts
140	.type	slave_handle_interrupts, @function
141slave_handle_interrupts:
142	call	DynarecSlaveHandleInterrupts
143	mov	slave_ip, %edx
144	mov	slave_cc, %esi
145	sub	%ebx, %esi
146	jmp	*%edx /* jmp *slave_ip */
147	.size	slave_handle_interrupts, .-slave_handle_interrupts
148
149.globl cc_interrupt
150	.type	cc_interrupt, @function
151cc_interrupt: /* slave */
152	mov	16(%esp), %ebx /* sh2cycles */
153	mov	%ebp, slave_ip
154	mov	%esi, slave_cc
155	add	$-12, %esp
156	push	%ebx
157	call	FRTExec
158	mov	%ebx, (%esp)
159	call	WDTExec
160	add	$16, %esp
161	.size	cc_interrupt, .-cc_interrupt
162.globl cc_interrupt_master
163	.type	cc_interrupt_master, @function
164cc_interrupt_master:
165	lea	40(%esp), %ebp
166	mov	-16(%ebp), %eax /* decilinecount */
167	mov	-20(%ebp), %ebx /* decilinecycles */
168	inc	%eax
169	cmp	$9, %eax
170	ja	.A3
171	mov	%eax, -16(%ebp) /* decilinecount++ */
172	je	.A2
173	mov	%ebx, -24(%ebp) /* sh2cycles */
174.A1:
175	mov	master_cc, %esi
176	mov	MSH2, %eax
177	mov	NumberOfInterruptsOffset, %ecx
178	cmp	$0, (%eax, %ecx)
179	jne	master_handle_interrupts
180	sub	%ebx, %esi
181	ret	/* jmp master_ip */
182.A2:
183	call	Vdp2HBlankIN
184	jmp	.A1
185.A3:
186	mov	-28(%ebp), %ebx /* scucycles */
187	add	$-12, %esp
188	push	%ebx
189	call	ScuExec
190	call	M68KSync
191	call	Vdp2HBlankOUT
192	call	ScspExec
193	mov	linecount_p, %ebx
194	mov	maxlinecount_p, %eax
195	mov	vblanklinecount_p, %ecx
196	mov	(%ebx), %edx
197	mov	(%eax), %eax
198	mov	(%ecx), %ecx
199	inc	%edx
200	andl	$0, -16(%ebp) /* decilinecount=0 */
201	cmp	%eax, %edx /* max ? */
202	je	nextframe
203	mov	%edx, (%ebx) /* linecount++ */
204	cmp	%ecx, %edx /* vblank ? */
205	je	vblankin
206nextline:
207	add	$16, %esp
208	call	finishline
209	jmp	newline
210finishline: /* CHECK - Stack align? */
211      /*const u32 usecinc = yabsys.DecilineUsec * 10;*/
212	mov	decilineusec_p, %eax
213	mov	UsecFrac_p, %ebx
214	mov	yabsys_timing_bits, %ecx
215	mov	(%eax), %eax
216	mov	(%ebx), %edx
217	lea	(%eax,%eax,4), %esi
218	mov	yabsys_timing_mask, %edi
219	add	%esi, %esi
220      /*yabsys.UsecFrac += usecinc;*/
221	add	%edx, %esi
222	add	$-8, %esp /* Align stack */
223      /*SmpcExec(yabsys.UsecFrac >> YABSYS_TIMING_BITS);
224      /*Cs2Exec(yabsys.UsecFrac >> YABSYS_TIMING_BITS);
225      /*yabsys.UsecFrac &= YABSYS_TIMING_MASK;*/
226	mov	%esi, (%ebx) /* UsecFrac */
227	shr	%cl, %esi
228	push	%esi
229	call	SmpcExec
230	/* SmpcExec may modify UsecFrac; must reload it */
231	mov	(%ebx), %esi /* UsecFrac */
232	mov	yabsys_timing_bits, %ecx
233	and	%esi, %edi
234	shr	%cl, %esi
235	mov	%esi, (%esp)
236	call	Cs2Exec
237	mov	%edi, (%ebx) /* UsecFrac */
238	mov	saved_centicycles, %ecx
239	mov	12(%ebp), %ebx /* m68kcenticycles */
240	mov	8(%ebp), %eax /* m68kcycles */
241	add	%ebx, %ecx
242	mov	%ecx, %ebx
243	add	$-100, %ecx
244	cmovnc	%ebx, %ecx
245	adc	$0, %eax
246	mov	%ecx, saved_centicycles
247	mov	%eax, (%esp) /* cycles */
248	call	M68KExec
249	add	$12, %esp
250	ret
251vblankin:
252	call	SmpcINTBACKEnd
253	call	Vdp2VBlankIN
254	call	CheatDoPatches
255	jmp	nextline
256nextframe:
257	call	Vdp2VBlankOUT
258	andl	$0, (%ebx) /* linecount = 0 */
259	call	finishline
260	call	M68KSync
261	mov	rccount, %esi
262	inc	%esi
263	andl	$0, invalidate_count
264	and	$0x3f, %esi
265	cmpl	$0, restore_candidate(,%esi,4)
266	mov	%esi, rccount
267	jne	.A5
268.A4:
269	mov	16(%esp), %eax
270	add	$44, %esp
271	mov	%eax, master_ip
272	pop	%ebx
273	pop	%esi
274	pop	%edi
275	pop	%ebp
276	ret
277.A5:
278	/* Move 'dirty' blocks to the 'clean' list */
279	mov	restore_candidate(,%esi,4), %ebx
280	mov	%esi, %ebp
281	andl	$0, restore_candidate(,%esi,4)
282	shl	$5, %ebp
283.A6:
284	shr	$1, %ebx
285	jnc	.A7
286	mov	%ebp, (%esp)
287	call	clean_blocks
288.A7:
289	inc	%ebp
290	test	$31, %ebp
291	jne	.A6
292	jmp	.A4
293	.size	cc_interrupt_master, .-cc_interrupt_master
294
295.globl dyna_linker
296	.type	dyna_linker, @function
297dyna_linker:
298	/* eax = virtual target address */
299	/* ebx = instruction to patch */
300	mov	%eax, %ecx
301	mov	$1023, %edx
302	shr	$12, %ecx
303	and	%ecx, %edx
304	and	$0xDFFFF, %ecx
305	or	$1024, %edx
306	cmp	%edx, %ecx
307	cmova	%edx, %ecx
308	/* jump_in lookup */
309	mov	jump_in(,%ecx,4), %edx
310.B1:
311	test	%edx, %edx
312	je	.B3
313	mov	(%edx), %edi
314	xor	%eax, %edi
315	je	.B2
316	movl	12(%edx), %edx
317	jmp	.B1
318.B2:
319	mov	(%ebx), %edi
320	mov	%esi, %ebp
321	lea	4(%ebx,%edi,1), %esi
322	mov	%eax, %edi
323	pusha
324	call	add_link
325	popa
326	mov	8(%edx), %edi
327	mov	%ebp, %esi
328	lea	-4(%edi), %edx
329	subl	%ebx, %edx
330	movl	%edx, (%ebx)
331	jmp	*%edi
332.B3:
333	/* hash_table lookup */
334	mov	%eax, %edi
335	shr	$16, %edi
336	xor	%eax, %edi
337	movzwl	%di, %edi
338	shl	$4, %edi
339	cmp	hash_table(%edi), %eax
340	jne	.B5
341.B4:
342	mov	hash_table+4(%edi), %edx
343	jmp	*%edx
344.B5:
345	cmp	hash_table+8(%edi), %eax
346	lea	8(%edi), %edi
347	je	.B4
348	/* jump_dirty lookup */
349	mov	jump_dirty(,%ecx,4), %edx
350.B6:
351	testl	%edx, %edx
352	je	.B8
353	mov	(%edx), %ecx
354	xor	%eax, %ecx
355	je	.B7
356	movl	12(%edx), %edx
357	jmp	.B6
358.B7:
359	mov	8(%edx), %edx
360	/* hash_table insert */
361	mov	hash_table-8(%edi), %ebx
362	mov	hash_table-4(%edi), %ecx
363	mov	%eax, hash_table-8(%edi)
364	mov	%edx, hash_table-4(%edi)
365	mov	%ebx, hash_table(%edi)
366	mov	%ecx, hash_table+4(%edi)
367	jmp	*%edx
368.B8:
369	mov	%eax, %edi
370	pusha
371	call	sh2_recompile_block
372	test	%eax, %eax
373	popa
374	je	dyna_linker
375	/* shouldn't happen */
376	int3
377	.size	dyna_linker, .-dyna_linker
378
379.globl jump_vaddr_eax_master
380	.type	jump_vaddr_eax_master, @function
381jump_vaddr_eax_master:
382	mov	%eax, %edi
383	jmp	jump_vaddr_edi_master
384	.size	jump_vaddr_eax_master, .-jump_vaddr_eax_master
385.globl jump_vaddr_ecx_master
386	.type	jump_vaddr_ecx_master, @function
387jump_vaddr_ecx_master:
388	mov	%ecx, %edi
389	jmp	jump_vaddr_edi_master
390	.size	jump_vaddr_ecx_master, .-jump_vaddr_ecx_master
391.globl jump_vaddr_edx_master
392	.type	jump_vaddr_edx_master, @function
393jump_vaddr_edx_master:
394	mov	%edx, %edi
395	jmp	jump_vaddr_edi_master
396	.size	jump_vaddr_edx_master, .-jump_vaddr_edx_master
397.globl jump_vaddr_ebx_master
398	.type	jump_vaddr_ebx_master, @function
399jump_vaddr_ebx_master:
400	mov	%ebx, %edi
401	jmp	jump_vaddr_edi_master
402	.size	jump_vaddr_ebx_master, .-jump_vaddr_ebx_master
403.globl jump_vaddr_ebp_master
404	.type	jump_vaddr_ebp_master, @function
405jump_vaddr_ebp_master:
406	mov	%ebp, %edi
407	jmp	jump_vaddr_edi_master
408	.size	jump_vaddr_ebp_master, .-jump_vaddr_ebp_master
409.globl jump_vaddr_eax_slave
410	.type	jump_vaddr_eax_slave, @function
411jump_vaddr_eax_slave:
412	mov	%eax, %edi
413	jmp	jump_vaddr_edi_slave
414	.size	jump_vaddr_eax_slave, .-jump_vaddr_eax_slave
415.globl jump_vaddr_ecx_slave
416	.type	jump_vaddr_ecx_slave, @function
417jump_vaddr_ecx_slave:
418	mov	%ecx, %edi
419	jmp	jump_vaddr_edi_slave
420	.size	jump_vaddr_ecx_slave, .-jump_vaddr_ecx_slave
421.globl jump_vaddr_edx_slave
422	.type	jump_vaddr_edx_slave, @function
423jump_vaddr_edx_slave:
424	mov	%edx, %edi
425	jmp	jump_vaddr_edi_slave
426	.size	jump_vaddr_edx_slave, .-jump_vaddr_edx_slave
427.globl jump_vaddr_ebx_slave
428	.type	jump_vaddr_ebx_slave, @function
429jump_vaddr_ebx_slave:
430	mov	%ebx, %edi
431	jmp	jump_vaddr_edi_slave
432	.size	jump_vaddr_ebx_slave, .-jump_vaddr_ebx_slave
433.globl jump_vaddr_ebp_slave
434	.type	jump_vaddr_ebp_slave, @function
435jump_vaddr_ebp_slave:
436	mov	%ebp, %edi
437	.size	jump_vaddr_ebp_slave, .-jump_vaddr_ebp_slave
438.globl jump_vaddr_edi_slave
439	.type	jump_vaddr_edi_slave, @function
440jump_vaddr_edi_slave:
441	or	$1, %edi
442	.size	jump_vaddr_edi_slave, .-jump_vaddr_edi_slave
443.globl jump_vaddr_edi_master
444	.type	jump_vaddr_edi_master, @function
445jump_vaddr_edi_master:
446	mov	%edi, %eax
447	.size	jump_vaddr_edi_master, .-jump_vaddr_edi_master
448
449.globl jump_vaddr
450	.type	jump_vaddr, @function
451jump_vaddr:
452  /* Check hash table */
453	shr	$16, %eax
454	xor	%edi, %eax
455	movzwl	%ax, %eax
456	shl	$4, %eax
457	cmp	hash_table(%eax), %edi
458	jne	.C2
459.C1:
460	mov	hash_table+4(%eax), %edi
461	jmp	*%edi
462.C2:
463	cmp	hash_table+8(%eax), %edi
464	lea	8(%eax), %eax
465	je	.C1
466  /* No hit on hash table, call compiler */
467	push	%edi
468	call	get_addr
469	add	$4, %esp
470	jmp	*%eax
471	.size	jump_vaddr, .-jump_vaddr
472
473.globl verify_code
474	.type	verify_code, @function
475verify_code:
476	/* eax = source */
477	/* ebx = target */
478	/* ecx = length */
479	mov	-4(%eax,%ecx,1), %edi
480	xor	-4(%ebx,%ecx,1), %edi
481	jne	.D5
482	mov	%ecx, %edx
483	add	$-4, %ecx
484	je	.D3
485	test	$4, %edx
486	cmove	%edx, %ecx
487	push	%esi
488.D2:
489	mov	-4(%eax,%ecx,1), %edx
490	mov	-4(%ebx,%ecx,1), %ebp
491	mov	-8(%eax,%ecx,1), %esi
492	xor	%edx, %ebp
493	mov	-8(%ebx,%ecx,1), %edi
494	jne	.D4
495	xor	%esi, %edi
496	jne	.D4
497	add	$-8, %ecx
498	jne	.D2
499	pop	%esi
500.D3:
501	ret
502.D4:
503	pop	%esi
504.D5:
505	add	$4, %esp /* pop return address, we're not returning */
506	call	get_addr
507	add	$4, %esp /* pop virtual address */
508	jmp	*%eax
509	.size	verify_code, .-verify_code
510
511.globl WriteInvalidateLong
512	.type	WriteInvalidateLong, @function
513WriteInvalidateLong:
514	mov	%eax, %ecx
515	shr	$12, %ecx
516	bt	%ecx, cached_code
517	jnc	MappedMemoryWriteLongNocache
518	push	%eax
519	push	%edx
520	push	%eax
521	call	invalidate_addr
522	pop	%eax
523	pop	%edx
524	pop	%eax
525	jmp	MappedMemoryWriteLongNocache
526	.size	WriteInvalidateLong, .-WriteInvalidateLong
527.globl WriteInvalidateWord
528	.type	WriteInvalidateWord, @function
529WriteInvalidateWord:
530	mov	%eax, %ecx
531	shr	$12, %ecx
532	bt	%ecx, cached_code
533	jnc	MappedMemoryWriteWordNocache
534	push	%eax
535	push	%edx
536	push	%eax
537	call	invalidate_addr
538	pop	%eax
539	pop	%edx
540	pop	%eax
541	jmp	MappedMemoryWriteWordNocache
542	.size	WriteInvalidateWord, .-WriteInvalidateWord
543.globl WriteInvalidateByteSwapped
544	.type	WriteInvalidateByteSwapped, @function
545WriteInvalidateByteSwapped:
546	xor	$1, %eax
547	.size	WriteInvalidateByteSwapped, .-WriteInvalidateByteSwapped
548.globl WriteInvalidateByte
549	.type	WriteInvalidateByte, @function
550WriteInvalidateByte:
551	mov	%eax, %ecx
552	shr	$12, %ecx
553	bt	%ecx, cached_code
554	jnc	MappedMemoryWriteByteNocache
555	push	%eax
556	push	%edx
557	push	%eax
558	call	invalidate_addr
559	pop	%eax
560	pop	%edx
561	pop	%eax
562	jmp	MappedMemoryWriteByteNocache
563	.size	WriteInvalidateByte, .-WriteInvalidateByte
564
565.globl div1
566	.type	div1, @function
567div1:
568	/* eax = dividend */
569	/* ecx = divisor */
570	/* edx = sr */
571	bt	$9, %edx   /* M bit */
572	jc	div1_negative_divisor
573	bts	$0, %edx   /* Get T bit and set */
574	adc 	%eax, %eax /* rn=(rn<<1)+T */
575	adc	%ebx, %ebx /* New Q in ebx */
576	mov	%ecx, %ebp
577	btr	$8, %edx   /* Get Q bit and clear it */
578	cmc
579	sbb	%edi, %edi /* 0xFFFFFFFF if old_Q clear, 0 otherwise */
580	sbb	$0, %ebp
581	xor	%edi, %ebp
582	add	%ebp, %eax /* rn+rm if old_Q, rn-rm if !old_Q */
583		           /* carry set if rn < old_rn */
584	adc	%edi, %ebx /* low bit = (rn<old_rn)^new_Q^!old_Q */
585	                   /* inverted for old_Q==0, ie (rn>=old_rn)^new_Q */
586	not	%edi	   /* if old_Q clear, edi=0 */
587	or	%ebp, %edi /* zero if old_Q==0 && rn==old_rn */
588	neg	%edi       /* clear carry if edi==0 */
589	adc	$-1, %ebx  /* invert result for old_Q==0 && rn==old_rn */
590	and	$1, %ebx
591	xor	%ebx, %edx /* New T = (Q==M) */
592	shl	$8, %ebx
593	or	%ebx, %edx /* save new Q */
594/*
595	push	%edx
596	push	%eax
597	push	%ecx
598	call	debug_division
599	pop	%ecx
600	pop	%eax
601	pop	%edx
602*/
603	ret
604div1_negative_divisor:
605	btr	$0, %edx   /* Get T bit and clear */
606	adc 	%eax, %eax /* rn=(rn<<1)+T */
607	adc	%ebx, %ebx /* New Q in ebx */
608	mov	%ecx, %ebp
609	btr	$8, %edx   /* Get Q bit and clear it */
610	sbb	%edi, %edi /* 0xFFFFFFFF if old_Q set, 0 otherwise */
611	sbb	$0, %ebp
612	xor	%edi, %ebp
613	not	%edi	   /* if old_Q clear, edi=-1 */
614	add	%ebp, %eax /* rn+rm if !old_Q, rn-rm if old_Q */
615		           /* carry set if rn < old_rn */
616	adc	%edi, %ebx /* low bit = (rn<old_rn)^new_Q^!old_Q */
617	                   /* inverted for old_Q==0, ie (rn>=old_rn)^new_Q */
618	or	%ebp, %edi /* zero if old_Q==1 && rn==old_rn */
619	neg	%edi       /* clear carry if edi==0 */
620	adc	$-1, %ebx  /* invert result for old_Q==1 && rn==old_rn */
621	and	$1, %ebx
622	xor	%ebx, %edx /* New T = (Q==M) */
623	shl	$8, %ebx
624	or	%ebx, %edx /* save new Q */
625	ret
626	.size	div1, .-div1
627
628.globl macl
629	.type	macl, @function
630macl:
631	/* ebx = sr */
632	/* ebp = multiplicand address */
633	/* edi = multiplicand address */
634	/* eax = return MACL */
635	/* edx = return MACH */
636	push	%edx /* MACH */
637	push	%eax /* MACL */
638	mov	%edi, %eax
639	call	MappedMemoryReadLongNocache
640	mov	%eax, %esi
641	mov	%ebp, %eax
642	call	MappedMemoryReadLongNocache
643	add	$4, %ebp
644	add	$4, %edi
645	imul	%esi
646	add	(%esp), %eax /* MACL */
647	adc	4(%esp), %edx /* MACH */
648	add	$8, %esp
649	test	$0x2, %bl
650	jne	macl_saturation
651	ret
652macl_saturation:
653	mov	$0xFFFF8000, %esi
654	xor	%ecx, %ecx
655	cmp	%esi, %edx
656	cmovl	%esi, %edx
657	cmovl	%ecx, %eax
658	not	%esi
659	not	%ecx
660	cmp	%esi, %edx
661	cmovg	%esi, %edx
662	cmovg	%ecx, %eax
663	ret
664	.size	macl, .-macl
665
666.globl macw
667	.type	macw, @function
668macw:
669	/* ebx = sr */
670	/* ebp = multiplicand address */
671	/* edi = multiplicand address */
672	/* eax = return MACL */
673	/* edx = return MACH */
674	push	%edx /* MACH */
675	push	%eax /* MACL */
676	mov	%edi, %eax
677	call	MappedMemoryReadWordNocache
678	movswl	%ax, %esi
679	mov	%ebp, %eax
680	call	MappedMemoryReadWordNocache
681	movswl	%ax, %eax
682	add	$2, %ebp
683	add	$2, %edi
684	imul	%esi
685	test	$0x2, %bl
686	jne	macw_saturation
687	add	(%esp), %eax /* MACL */
688	adc	4(%esp), %edx /* MACH */
689	add	$8, %esp
690	ret
691macw_saturation:
692	mov	(%esp), %esi
693	sar	$31, %esi
694	add	(%esp), %eax /* MACL */
695	adc	%esi, %edx
696	mov	$0x80000000, %esi
697	mov	$0x7FFFFFFF, %ecx
698	add	%eax, %esi
699	adc	$0, %edx
700	cmovne	%ecx, %eax
701	not	%ecx
702	cmovl	%ecx, %eax
703	pop	%edx
704	pop	%edx
705	ret
706	.size	macw, .-macw
707
708.globl master_handle_bios
709	.type	master_handle_bios, @function
710master_handle_bios:
711	mov	(%esp), %edx /* get return address */
712	mov	%eax, master_pc
713	mov	%esi, master_cc
714	mov	%edx, master_ip
715	mov	MSH2, %eax
716	call	BiosHandleFunc
717	mov	master_ip, %edx
718	mov	master_cc, %esi
719	mov	%edx, (%esp)
720	ret	/* jmp *master_ip */
721	.size	master_handle_bios, .-master_handle_bios
722
723.globl slave_handle_bios
724	.type	slave_handle_bios, @function
725slave_handle_bios:
726	pop	%edx /* get return address */
727	mov	%eax, slave_pc
728	mov	%esi, slave_cc
729	mov	%edx, slave_ip
730	mov	SSH2, %eax
731	call	BiosHandleFunc
732	mov	slave_ip, %edx
733	mov	slave_cc, %esi
734	jmp	*%edx /* jmp *slave_ip */
735	.size	slave_handle_bios, .-slave_handle_bios
736
737.globl breakpoint
738	.type	breakpoint, @function
739breakpoint:
740	ret
741	/* Set breakpoint here for debugging */
742	.size	breakpoint, .-breakpoint
743