dos_compilers/Manx Aztec C86 v52a/LIB/ROM86/ABEGINLD.ASM
2024-07-02 08:25:54 -07:00

496 lines
13 KiB
NASM

; ABEGINLD.ASM - Aztec Embedded Systems Startup Code for 'large data' programs
; Copyright (C) 1984-1992 by Manx Software Systems
; :ts=8
include lmacros.h
;
; Equates:
ifndef NEAR_DATA_INIT
NEAR_DATA_INIT equ 1 ; 0=Disable init. of _DATA, 1=enable.
endif
ifndef FAR_DATA_INIT
FAR_DATA_INIT equ 0 ; 0=Disable init. of FAR_DATA, 1=enable.
endif
ifndef FAR_BSS_INIT
FAR_BSS_INIT equ 0 ; Set to 1 to enable initialization of FAR_BSS
endif
ifndef CPU_186
CPU_186 equ 0 ; 0=CPU is not 80186, 1=CPU is 80186.
endif
;
; STACK PLACEMENT
; ---------------
; If you want your stack at a fixed place, you may remove the
; "bss cstack..." statement below and change the "mov sp,cstack..."
; to load SP with the value you desire. Note that the setup of
; SS may need to be changed also. If the program is small data
; model, then SS must be equal to DS or pointers to automatic
; variables on the stack won't work properly. In large data model,
; there are no restrictions on stack placement.
;
; Otherwise, stacksize should be set according to your program's
; requirements.
;
stacksize equ 80h ; # paras in stack area
heapsize equ 80h ; # paras in heap area
;
; 80186 CHIP SELECT DEFINITIONS
; -----------------------------
;
if CPU_186
; chip select registers
UMCS equ 0FFA0h ;Upper memory chip select
LMCS equ 0FFA2h ;Lower memory chip select
PACS equ 0FFA4h ;Peripheral chip select
MPCS equ 0FFA8h ;Middle-memory chip select
;
; Valid values for UMCS
; The equate for ROM_SZ (below) should be changed to reflect your system's
; available high-memory ROM size.
;
UMBS_1 equ 0FFF8h ;1K
UMBS_2 equ 0FFB8h ;2K
UMBS_4 equ 0FF38h ;4K
UMBS_8 equ 0FE38h ;8K
UMBS_16 equ 0FC38h ;16K
UMBS_32 equ 0F838h ;32K
UMBS_64 equ 0F038h ;64K
UMBS_128 equ 0E038h ;128K
UMBS_256 equ 0C038h ;256K
;
; Valid values for LMCS
; The equate for RAM_SZ (below) should be changed to reflect your
; system's available low-memory RAM size.
;
LMBS_1 equ 0038h ;1K
LMBS_2 equ 0078h ;2K
LMBS_4 equ 00f8h ;4K
LMBS_8 equ 01f8h ;8K
LMBS_16 equ 03f8h ;16K
LMBS_32 equ 07f8h ;32K
LMBS_64 equ 0ff8h ;64K
LMBS_128 equ 1ff8h ;128K
LMBS_256 equ 3ff8h ;256K
endif
dataseg segment word public 'data'
;
; RUN-TIME LIBRARY VARIABLES
; --------------------------
; Variables required by the run-time libraries for heap management, error
; reporting, etc.
;
global errno_:word,2
global _dsval_:word,2
global _csval_:word,2
global _mbot_:word,4 ; ptr to base of heap area
global _mcur_:word,4 ; ptr to heap 'high water' mark
global _mtop_:word,4 ; ptr to top of heap area
global _sbot_:word,4
;
; The variable is used with csav and cret only. Therefore if you do not
; compile the the +c option or +b option you do not need this.
;
public _lowwater_
_lowwater_ dw -1
;
; These variables are used to 'bracket' the code, data, and unitialized
; data segments. They are initialized by the locator.
;
extrn _Dorg_:byte,_Dend_:byte
extrn _Uorg_:byte,_Uend_:byte
dataseg ends
extrn _Corg_:byte,_Cend_:byte
ifdef FARPROC
extrn main_:far, $fltinit:far
else
extrn main_:near, $fltinit:near
endif
;------------------------------------------
; $BEGIN - C initialization routine ;
;------------------------------------------
public $begin
$begin proc far
cli
cld
if CPU_186
;
; 80186 LOW-MEMORY CHIP SELECT INITIALIZATION
; -------------------------------------------
; This code intializes the LMCS register, and assumes an 8K
; RAM size at lower memory. If your RAM size is different, change
; the RAM_SZ equate below to the appropriate LMBS_X defintion.
;
RAM_SZ equ LMBS_8 ; This assumes a 8K RAM area
mov dx,LMCS
mov ax,RAM_SIZE
out dx,ax
;
; Additional 80186 chip select and peripheral chip select operations
; should be done here.
;
endif
;
; COMPUTE WHERE INITIALZED DATA STARTS
; ------------------------------------
; This code sets up the following registers:
; DS - Paragraph address of the end of the 'CODE' class.
; ES - Paragraph address of the start of the _DATA.
;
; Aloc guarantees that class bracket symbols are normalized
; (i.e., _Cend_'s offset will never be greater than 0x0f), so we have
; nice tiny code.
mov ax,seg _Cend_
mov ds,ax
mov bx,seg _Dorg_
mov es,bx ; Note well: BX now has _DATA's para #
;
; STACK SETUP
; -----------
; This code puts the stack in the uninitialized data area i.e., its
; eating up bytes in DGROUP. This is a necessary evil in the small
; data model, but in large data the stack can be anywhere you'd like.
;
; Note: Loading of SP must immediately follow the loading of SS
;
mov ax,offset _Uend_ ; stack starts at Uend
add ax,15 ; round up to next paragraph
mov cl,4 ; and convert to paragraph form
shr ax,cl
and ax,0fffh
mov dx,es ; add in datasegment base paragraph
add ax,dx
mov ss,ax
mov sp,stacksize shl 4
;
; COPY NEAR INITIALIZED DATA FROM ROM TO RAM
; ------------------------------
;
; This code assumes that the _DATA segment immediately follows
; the _TEXT segment in ROM (that is, _Dorg_==_Cend_), and needs
; to be relocated to its proper address in RAM.
; This copying might not be necessary. For example, if RDB downloads
; the .LOC file, it places _DATA at the correct address.
; To prevent this copying, define the symbol NO_DATA_INIT.
;
; ALOC guarantees that segment-bracket symbols will have the same
; paragraph address, and we take advantage of that here.
if NEAR_DATA_INIT
mov si,offset _Cend_ ; Get src addr into DS:SI
mov di,offset _Dorg_ ; Get dest addr into ES:DI
mov cx,offset _Dend_ ; Get # words to copy into CX
sub cx,di
mov dx,cx ; Get # bytes to copy into DX
shr cx,1
jcxz d00
rep movsw ; Copy the sucker
d00: ; ES:DI has addr of _Dend_
test dx,1 ; Odd number of bytes?
jz d10 ; Jump if not
movsb ; Copy the last byte
d10:
endif
; COPY FAR INITIALIZED DATA FROM ROM TO RAM
; ----------------------------------
; To have the FAR_DATA class copied, define FAR_INIT.
;
if FAR_DATA_INIT
call far_init
endif
; CLEAR FAR UNINITIALIZED DATA SEGMENT
; ------------------------------------
; To have the FAR_BSS class cleared, equate FAR_BSS_INIT to 1.
;
if FAR_BSS_INIT
call far_clear
endif
;
; ZERO-OUT UNINITIALIZED DATA
; ---------------------------
; Clear uninitialized data. This step should always be performed, since
; C guarantees that uninitialized data will contain 0 upon startup.
;
mov di,offset _Uorg_ ; Get addr of _BSS seg into ES:DI
mov cx,offset _Uend_ ; Get # words to copy into CX
mov dx,cx ; Get # bytes to copy into DX
sub cx,di
shr cx,1
jcxz b00
sub ax,ax ; We're stuffing 0's in the BSS area
rep stosw ; Zero it out
b00:
test dx,1 ; Odd number of bytes?
jz b10 ; Jump if not
stosb ; Clear the last byte
b10:
;
assume ds:dataseg,es:dataseg
mov ax,es
mov ds,ax ; BX has _DATA's para #, remember?
mov _dsval_,bx
mov _csval_,cs ;this is of dubious value in large code
;
; HEAP SETUP
; ----------
; This sets up various variables required by the memory allocation
; routines in the run-time library (malloc(), free(), etc.).
;
mov ax,ss ;heap begins after stack
add ax,stacksize
mov _mbot_+2,ax ;set up pointer to bottom of heap area
mov _mcur_+2,ax ;set up pointer to bottom of heap area
add ax,heapsize ;add in heap size
mov _mtop_+2,ax ;set up pointer to top of heap area
sti
call $fltinit ;setup floating point software/hardware
jnc flt_ok
hlt ;program needs 8087 and one wasn't found
flt_ok:
jmp main_ ;main usually doesn't return in ROM based system
$begin endp
;
; END OF $BEGIN
; -------------
;----------------------------------------------------------------------------
;
; 80186 INITIALIZATION CODE
; -------------------------
; If your target system is an 8018X machine (80186, 80188, etc.), you
; will in most likelihood have to turn on your main ROM's memory chip
; select at the reset vector address (ffff:0). Sample code for doing
; this and then jumping to the entry point is provided here. Remember,
; you have only 16 bytes in which to do this, so only do absolutely
; necessary initializations here - all other initializations can be
; done within $begin (such as additional chip selects).
;
; This example code sets the upper memory code select (UMCS) to be
; 8k wide. (i.e., it assumes you've got a 8K ROM at the top-most
; portion of the target's memory address space). If your ROM is a
; different size, use the appropriate MBS_X defintion (where 'x' is the
; size of your ROM, in kilobytes) for the ROM_SZ equate.
;
; Once the chip select is setup, it immediately jumps to the main
; initialization code at $begin.
;
;----------------------------------------------------------------------------
if CPU_186
ROM_SZ equ MBS_8 ; Assume a 8K ROM.
RESET_CODE segment AT 0ffffh
mov dx,UMCS
mov ax,MBS_8
out dx,ax
jmp $begin
RESET_CODE ends
else
;----------------------------------------------------------------------------
; GENERIC 80x86 reset code
; ------------------------
; This is typical reset code for a non-80186/80188 system, where we
; simply jump to $begin.
;
; NOTE: There are only 16 bytes (that's bytes, not k-bytes!!) available
; in the RESET_CODE segment, so only absolutely necessary initialization
; should be done here. Most initializations should instead go into
; the $begin routine.
;----------------------------------------------------------------------------
RESET_CODE segment AT 0ffffh
jmp $begin
RESET_CODE ends
endif
;----------------------------------------------------------------------------
;FAR_INIT - Copy Far initialized data from ROM to RAM. ;
; ;
; On entry, ES:DI points at the first byte after the _DATA segment in ROM.
; By default, this is where AHEX places the program's FAR_DATA class. If
; FAR_DATA is somewhere else in ROM, this code will have to be changed.
;
; far_init makes no assumptions about the starting position of the source
; and destination blocks, or the length. This code could thus be optimized
; if assumptions can be made (such as length<64k, starting addrs on para
; boundary).
;----------------------------------------------------------------------------
if FAR_DATA_INIT
extrn _FDorg_:byte, _FDend_:byte
ifdef FARPROC
extrn _ptrdiff_:far
else
extrn _ptrdiff_:near
endif
far_init proc near
; Preserve regs...
push bx
push es
push ds
; Load registers for copy...
mov ax,seg _FDorg_ ; Get len of FAR_DATA into DX,CX
push ax
mov ax,offset _FDorg_
push ax
mov ax,seg _FDend_
push ax
mov ax,offset _FDend_
push ax
call _ptrdiff_
add sp,8
mov cx,ax
mov ax,es ; Get 'from' addr into DS:SI
mov ds,ax
mov si,di
mov ax,seg _FDorg_ ; Get 'to' addr into ES:DI
mov es,ax
mov di,offset _FDorg_
; Copy...
ch00:
movsb ;copy one byte
test si,si ;incr src para reg when necessary
jnz ch10
mov ax,ds
add ax,1000h
mov ds,ax
ch10:
test di,di ;incr dest para reg when necessary
jnz ch20
mov ax,es
add ax,1000h
mov es,ax
ch20:
add cx,-1 ;decr counter
adc dx,-1
mov ax,cx ;anything left to copy?
or ax,dx
jnz ch00 ;loop if yes
; Restore regs...
pop ds
pop es
pop bx
; Return to caller...
ret
far_init endp
endif
;
;----------------------------------------------------------------------------
;FAR_CLEAR - Clear far uninitialized data
;
; far_clear makes no assumptions about the starting position of the source
; and destination blocks, or the length. This code could thus be optimized
; if assumptions can be made (such as length<64k, starting addrs on para
; boundary).
;----------------------------------------------------------------------------
if FAR_BSS_INIT
extrn _FUorg_:byte, _FUend_:byte
ifdef FARPROC
extrn _ptrdiff_:far
else
extrn _ptrdiff_:near
endif
far_clear proc near
; Preserve regs...
push bx
push es
push ds
; Load registers for clear ...
mov ax,seg _FUorg_ ; Get len of FAR_BSS class into DX,CX
push ax
mov ax,offset _FUorg_
push ax
mov ax,seg _FUend_
push ax
mov ax,offset _FUend_
push ax
call _ptrdiff_
add sp,8
mov cx,ax
mov ax,seg _FUorg_ ; Get far bss addr into ES:DI
mov es,ax
mov di,offset _FUorg_
; Clear...
xor ax,ax
bh00:
stosb ;clear one byte
test di,di ;incr dest para reg when necessary
jnz bh20
mov bx,es
add bx,1000h
mov es,bx
bh20:
add cx,-1 ;decr counter
adc dx,-1
mov bx,cx ;anything left to copy?
or bx,dx
jnz bh00 ;loop if yes
; Restore regs...
pop ds
pop es
pop bx
; Return to caller...
ret
far_clear endp
endif
;
codeseg ends
;
; $begin is the default entry point. If for reason you wish to change
; this, provide the entry routine in this file, and replace the name
; '$begin' after the end keyword with the name of your routine.
;
end $begin