
;    Copyright Ad Lib Inc., 1990

;    This file is part of the Ad Lib Programmer's Manual product and is
;    subject to copyright laws.  As such, you may make copies of this file
;    only for the purpose of having backup copies.  This file may not be
;    redistributed in any form whatsoever.

;    If you find yourself in possession of this file without having received
;    it directly from Ad Lib Inc., then you are in violation of copyright
;    laws, which is a form of theft.


; SETFREQ.ASM
;
; Adlib Inc, 2-mar-90
;
; The routine of this module is
;   int SetFreq (voice, note, pitch, keyOn)
; and is used to send frequency information to the sound board, according
; to the note, pitch bend and pitch bend range.
;
; Normally called by module ADLIB.C


.MODEL LARGE

; @codesize is predefined by MASM: 0 = small, compact; 1 = med, large, huge
IF  @codesize
   CPSIZE  EQU 4
ELSE
   CPSIZE  EQU 2
ENDIF


; ====================== UNINITALIZED DATA SEGMENT =========================
.DATA?

EXTRN   _pitchRange:WORD    ; pitch bend range (global)



; ======================= INITALIZED DATA SEGMENT ==========================
.DATA

; TABLE: "fNumTbl" (below)

; This table contains the 10 bit f-numbers that are used to set the
; frequency in the registers 0xA0 and 0xB0.  The table contains f-numbers
; for the 12 half-tones in an octave, with a resolution of 16 steps between
; each half-tone.  There are then 12 * 16 = 192 entries in the table.

; The following C code is the code originally used to generate the table.

; If the f-number is negative, 1 must be added to the block octave
; information.

; Each value is computed using the formula:
; fN = (2**20 x Fmus) / (3.58e6 / 72)
; fN is shifted right until is below 1024 (10 bits)


;#define CLOCK			3579545L /* 14.31818e6 /4 */
;#define F_INTERN        (CLOCK /72) /* internal chip freq */
;#define NB_NOTES		96
;#define OCTAVE			12
;#define NB_TABLE_DEMI_TON OCTAVE
;#define NB_STEP_PITCH	16		/* 16 pas d'un ton a l'autre */
;#define LOG_NB_STEP_PITCH 4		/* LOG2( NB_STEP_PITCH) */
;#define TABLE_SIZE (NB_STEP_PITCH * NB_TABLE_DEMI_TON)
;#define LOG_PITCH		8	/* LOG2( TABLE_SIZE) */
;#define FREQ_DO (double)261.6256	/* reference, was 260.44 Hz ... */
;
;#define xexpy( x, y) (exp( y * log( x)))	/* return x**y */
;
;GenTable()
;	{
;	double freq;
;	double range;
;	long fNum, fN10;
;	int block;
;	int i, value;
;
;	range = 2.0;
;	for( i = 0; i < TABLE_SIZE; i++) {
;		freq = xexpy( range, (double)i / TABLE_SIZE);
;		freq *= FREQ_DO;
;       fNum = ((long)((long)1 << 20) * freq) /F_INTERN;
;		for( block = 0, fN10 = fNum; fN10 >= 1024; fN10 >>= 1, block++)
;			;
;		/* block range from 3 to 4 */
;		fN10 = (fNum + (1 << block -1)) >> block; /* round to 0.5 */
;		block = 3 - block;
;		value = fN10 | (block << 10);
;		printf( "%05xH, ", value);
;		if( i % 10 == 9)
;			printf( "\n");
;		}
;	printf( "\n\n");
;	}


fNumTbl dw 02b2H
	dw        02b4H, 02b7H, 02b9H, 02bcH, 02beH, 02c1H, 02c3H, 02c6H, 02c9H
	dw 02cbH, 02ceH, 02d0H, 02d3H, 02d6H, 02d8H, 02dbH, 02ddH, 02e0H, 02e3H 
	dw 02e5H, 02e8H, 02ebH, 02edH, 02f0H, 02f3H, 02f6H, 02f8H, 02fbH, 02feH 
	dw 0301H, 0303H, 0306H, 0309H, 030cH, 030fH, 0311H, 0314H, 0317H, 031aH 
	dw 031dH, 0320H, 0323H, 0326H, 0329H, 032bH, 032eH, 0331H, 0334H, 0337H 
	dw 033aH, 033dH, 0340H, 0343H, 0346H, 0349H, 034cH, 034fH, 0352H, 0356H 
	dw 0359H, 035cH, 035fH, 0362H, 0365H, 0368H, 036bH, 036fH, 0372H, 0375H 
	dw 0378H, 037bH, 037fH, 0382H, 0385H, 0388H, 038cH, 038fH, 0392H, 0395H 
	dw 0399H, 039cH, 039fH, 03a3H, 03a6H, 03a9H, 03adH, 03b0H, 03b4H, 03b7H 
	dw 03bbH, 03beH, 03c1H, 03c5H, 03c8H, 03ccH, 03cfH, 03d3H, 03d7H, 03daH 
	dw 03deH, 03e1H, 03e5H, 03e8H, 03ecH, 03f0H, 03f3H, 03f7H, 03fbH, 03feH 
	dw 0fe01H, 0fe03H, 0fe05H, 0fe07H, 0fe08H, 0fe0aH, 0fe0cH, 0fe0eH, 0fe10H, 0fe12H
	dw 0fe14H, 0fe16H, 0fe18H, 0fe1aH, 0fe1cH, 0fe1eH, 0fe20H, 0fe21H, 0fe23H, 0fe25H 
	dw 0fe27H, 0fe29H, 0fe2bH, 0fe2dH, 0fe2fH, 0fe31H, 0fe34H, 0fe36H, 0fe38H, 0fe3aH 
	dw 0fe3cH, 0fe3eH, 0fe40H, 0fe42H, 0fe44H, 0fe46H, 0fe48H, 0fe4aH, 0fe4cH, 0fe4fH 
	dw 0fe51H, 0fe53H, 0fe55H, 0fe57H, 0fe59H, 0fe5cH, 0fe5eH, 0fe60H, 0fe62H, 0fe64H 
	dw 0fe67H, 0fe69H, 0fe6bH, 0fe6dH, 0fe6fH, 0fe72H, 0fe74H, 0fe76H, 0fe79H, 0fe7bH 
	dw 0fe7dH, 0fe7fH, 0fe82H, 0fe84H, 0fe86H, 0fe89H, 0fe8bH, 0fe8dH, 0fe90H, 0fe92H 
	dw 0fe95H, 0fe97H, 0fe99H, 0fe9cH, 0fe9eH, 0fea1H, 0fea3H, 0fea5H, 0fea8H, 0feaaH 
	dw 0feadH, 0feafH 

; Integer division by 12 table (96 entries).  It is used to find the octave
; for a given note value in the range [0, 95].
noteDIV12 db 0
	db     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1
	db 1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2 
	db 2,  2,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4 
	db 4,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  5 
	db 5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7 
	db 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7

; Integer modulo 12 table (96 entries).  It is used to find the half-tone
; value of a note ([0, 95]) within an octave.
noteMOD12 db 0
	db     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4
	db 5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9
	db 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2
	db 3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7
	db 8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0
	db 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11



;========================== CODE SEGMENT ============================
.CODE

EXTRN   _SndOutput:PROC      ; SndOutput (addr, data); outputs 'data' at
							 ;   register 'addr'


;#define NB_NOTES		96	/* 8 octave of 12 notes */
;#define OCTAVE			12	/* half-tone by octave */
;#define NB_TABLE_DEMI_TON OCTAVE
;#define NB_STEP_PITCH	16		/* 16 steps between two half-tones */
;#define LOG_NB_STEP_PITCH 4		/* LOG2( NB_STEP_PITCH) */
;#define TABLE_SIZE (NB_STEP_PITCH * NB_TABLE_DEMI_TON)
;#define LOG_PITCH		8	/* LOG2( TABLE_SIZE) */

	nb_notes equ	96
	octave	equ	12
	nb_table_demi_ton equ octave
	nb_step_pitch	equ 16
	log_nb_step_pitch equ 4
	table_size equ	(nb_step_pitch * nb_table_demi_ton)
	log_pitch equ	8


	KEYON_BLOCK_FNUM equ 0B0H
	FNUM_LOW	equ 0A0H

;-------------------------------------------------------------------------
; Set the frequency of voice 'voice' to note number 'note', shifting
; the note by 'pitch/0x2000' of 'pitchRange' (global, 1-12). The key-on bit
; is set to the passed 'keyOn' value.
; Returns the value written to register 0xB0+voice.

;int SetFreq( voice, note, pitch, keyOn)
; int voice;        /* [0 - 8] */
; unsigned note;	/* [0, 95] */
; int pitch;		/* 14 bits, [0, 3fff] */
; int keyOn;		/* key on bit-feild == 0 or 0x20 */
; {
; int tblValue, tNote;
; unsigned t1, t2;
; unsigned tableOff;
; unsigned fNLow, fNHigh;
; int block;
; int signP;

public   _SetFreq

_SetFreq  proc

SF_F struc
sf_local dw	(?)	; old bp
		db  CPSIZE DUP (?)  ; return addr
voice	dw	(?)
note	dw	(?)
pitch	dw	(?)
keyOn	dw	(?)
SF_F ends

	push	bp
	mov	bp, sp
	IF sf_local NE 0
	sub	sp, sf_local
	ENDIF

; /* Convert unsigned pitch bend to [-0x2000, +0x2000] signed. */
; signP = (int)pitch - 0x2000;
	mov	ax, [bp].pitch
	sub	ax, 2000H
	je	after_mul		; if 0, by-pass multiplication...

; /* Keep the 8 most significant bits of the pitch bend. */
; t2 = signP >> (13 -LOG_PITCH);
	sar	ax, 1
	sar	ax, 1
	sar	ax, 1
	sar	ax, 1
	sar	ax, 1

; /* Multiply the modified pitch bend by the pitch bend range to obtain the
;    signed note number offset in 1/256's (8 bit fix point). */
; t2 *= pBRange;
	IF 0
	imul    DGROUP: _pitchRange ; t2

	ELSE
	; code to avoid multiplication: will be faster on 8086 machines...
	mov	cx, ax
	mov bx, DGROUP: _pitchRange
	shl	bx, 1
	neg	bx
	add	bx, offset CS: add_table+2
	jmp	bx
	EVEN
	add	ax, cx		; x 12
	add	ax, cx		; x 11
	add	ax, cx		; x 10
	add	ax, cx		; x 9
	add	ax, cx		; x 8
	add	ax, cx		; x 7
	add	ax, cx		; x 6
	add	ax, cx		; x 5
	add	ax, cx		; x 4
	add	ax, cx		; x 3
	add	ax, cx		; x 2
add_table:			; x 1
	ENDIF

; /* convert note number in 8 bit fix point and add preceding computed
; note offset: */
; t1 = note << LOG_PITCH;
; tNote = (t1 + t2);
after_mul:
	add	ah, byte ptr [bp].note

; /* round note number to 1/16  (by adding 1/32): */
; tNote += (1 << LOG_PITCH - LOG_NB_STEP_PITCH -1);
	add ax, (1 SHL (log_pitch - log_nb_step_pitch - 1))

; /*
;   Convert 8 bit fix point to 4 bit fix point (1/16).
;   'tNote' is then the resulting note number after adjusting for pitch bend,
;   expressed in 1/16 half-tones.
; */
; tNote >>= LOG_PITCH - LOG_NB_STEP_PITCH;
	sar	ax, 1
	sar	ax, 1
	sar	ax, 1
	sar	ax, 1

; /* Make sure that the note is in the range [0, 95]. */
; if( tNote < 0)
	jge	l3

; tNote = 0;
	xor	ax, ax
	jmp	l4

; /* Compare with 96 in 4 bit fix point. */
; if( tNote >= NB_NOTES << LOG_NB_STEP_PITCH)
l3:	cmp	ax, (nb_notes SHL log_nb_step_pitch)-1
	jl	l4

; tNote = (NB_NOTES << LOG_NB_STEP_PITCH) -1;
	mov	ax, (nb_notes SHL log_nb_step_pitch)-1

; /* Get half-tone value within 1 octave by using integer part
;    of note number MODULO 12. */
; tableOff = noteMOD12[ (tNote >> LOG_NB_STEP_PITCH)];
l4:
	mov	di, ax
	shr	di, 1
	shr	di, 1
	shr	di, 1
	shr	di, 1
	mov	dx, di		; tNote >> log_nb_step_pitch
	mov bl, noteMOD12[di]
	xor	bh, bh
	mov	di, bx

; /* Convert half-tone number to offset in table 'tableOff', in order
; to point to start of data for this half-tone.
; Since each half-tone consisting of 16 entries of 2 bytes, we multiply by
; 32. */
; tableOff <<= LOG_NB_STEP_PITCH +1;
	shl	di, 1
	shl	di, 1
	shl	di, 1
	shl	di, 1
	shl	di, 1

; /* Add to this offset the fractional part (low order 4 bits)
; of note number multiplied by 2 to obtain the exact offset in 'tableOff'
; for the note number. */
; tableOff += (tNote << 1) & (NB_STEP_PITCH *2 -1);
	shl	ax, 1
	and	ax, (nb_step_pitch *2 -1)
	add	di, ax

; /* Get frequency information pointed to... */
; tblValue =* (int *)((char *)fNumTbl +tableOff);
	mov ax, fNumTbl[di]

; /* Get octave number using integer part of note number and DIV 12 table. */
; block = noteDIV12 [(tNote >> LOG_NB_STEP_PITCH)] -1;
	mov	di, dx
	mov bl, noteDIV12[di]
	dec	bl

; /* If high bit of frequency information is set, this means that we must
; add 1 to octave number (frequency information has been divided by 2). */
; block += (tblValue < 0) ? 1 : 0;
	or	ax, ax
	jge	l5
	inc	bl

; if (block < 0) {
l5:
	or	bl, bl
	jge	l6

; block++;
	inc	bl

; tblValue >>= 1;
	sar	ax, 1
l6:

;/* Send low bits of frequency information to sound board. */
; SndOutput (0xA0 +voice, tblValue & 255);
	push	bx		; save block
	push	ax		; save tblValue
	xor	ah, ah
	push	ax
	mov	al, FNUM_LOW
	add	al, byte ptr [bp].voice
	push	ax
	call	_SndOutput
	add	sp, 4
	pop	ax		; restore tblValue
	pop	bx		; restore block


; /* Send 'keyOn' bit, octave number and 2 high bits of frequency information
;    to sound board. */
; SndOutput (0xB0 +voice, keyOn + (block << 2) + (tblValue >> 8) & 3);
	mov	al, ah
	and	al, 3
	shl	bl, 1
	shl	bl, 1
	add	al, bl
	add	al, byte ptr [bp].keyOn
	xor	ah, ah
	push	ax		; save return value
	push	ax
	mov	ax, KEYON_BLOCK_FNUM
	add	ax, [ bp].voice
	push	ax
	call	_SndOutput
	add	sp, 4
	pop	ax		; retore return value

; }
	IF sf_local NE 0
	add	sp, sf_local
	ENDIF
	pop	bp
	ret

_SetFreq  endp

	end



