248 lines
5 KiB
NASM
248 lines
5 KiB
NASM
|
; SCCSID = @(#)ibmhalo.asm 1.1 85/04/10
|
||
|
; On 2K (800h) boundaries beginning at address C0000h and ending at EF800h
|
||
|
; there is a header that describes a block of rom program. This header
|
||
|
; contains information needed to initialize a module and to provide PCDOS
|
||
|
; with a set of reserved names for execution.
|
||
|
;
|
||
|
; This header has the following format:
|
||
|
;
|
||
|
; rom_header STRUC
|
||
|
; Signature1 DB 55h
|
||
|
; Signature2 DB AAh
|
||
|
; rom_length DB ? ; number of 512 byte pieces
|
||
|
; init_jmp DB 3 dup (?)
|
||
|
; name_list name_struc <>
|
||
|
; rom_header ENDS
|
||
|
;
|
||
|
; name_struc STRUC
|
||
|
; name_len DB ?
|
||
|
; name_text DB ? DUP (?)
|
||
|
; name_jmp DB 3 DUP (?)
|
||
|
; name_struc ENDS
|
||
|
;
|
||
|
; The name list is a list of names that are reserved by a particular section
|
||
|
; of a module. This list of names is terminated by a null name (length
|
||
|
; is zero).
|
||
|
;
|
||
|
; Consider now, the PCDOS action when a user enters a command:
|
||
|
;
|
||
|
; COMMAND.COM has control.
|
||
|
; o If location FFFFEh has FDh then
|
||
|
; o Start scanning at C0000h, every 800h for a byte 55h followed
|
||
|
; by AAh, stop scan if we get above or = F0000H
|
||
|
; o When we've found one, compare the name entered by the user
|
||
|
; with the one found in the rom. If we have a match, then
|
||
|
; set up the environment for execution and do a long jump
|
||
|
; to the near jump after the found name.
|
||
|
; o If no more names in the list, then continue scanning the module
|
||
|
; for more 55h followed by AAh.
|
||
|
; o We get to this point only if there is no matching name in the
|
||
|
; rom. We now look on disk for the command.
|
||
|
;
|
||
|
; This gives us the flexibility to execute any rom cartridge without having
|
||
|
; to 'hard-code' the name of the cartridge into PCDOS. Rom modules that
|
||
|
; want to be invisible to the DOS should not have any names in their lists
|
||
|
; (i.e. they have a single null name).
|
||
|
;
|
||
|
; Consider a new release of BASIC, say, that patches bugs in the ROM version.
|
||
|
; Clearly this version will be available on disk. How does a user actually
|
||
|
; invoke this new BASIC?? He cannot call it BASIC on the disk because the
|
||
|
; EXEC loader will execute the ROM before it even looks at the disk! Only
|
||
|
; solution:
|
||
|
;
|
||
|
; o Keep things consistent and force the user to have his software named
|
||
|
; differently from the ROM names (BASIC1, BASIC2, etc).
|
||
|
|
||
|
rom_header STRUC
|
||
|
Signature1 DB ?
|
||
|
Signature2 DB ?
|
||
|
rom_length DB ?
|
||
|
init_jmp DB 3 dup (?)
|
||
|
name_list DB ?
|
||
|
rom_header ENDS
|
||
|
|
||
|
name_struc STRUC
|
||
|
name_len DB ?
|
||
|
name_text DB 1 DUP (?)
|
||
|
name_jmp DB 3 DUP (?)
|
||
|
name_struc ENDS
|
||
|
|
||
|
ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
|
||
|
|
||
|
;
|
||
|
; Check for IBM PC Jr rom cartrides. DS:DX is a pointer to name
|
||
|
;
|
||
|
ROM_SCAN:
|
||
|
PUSH ES
|
||
|
PUSH SI
|
||
|
PUSH DI
|
||
|
PUSH CX
|
||
|
PUSH AX
|
||
|
PUSH BX
|
||
|
;
|
||
|
; check for PC Jr signature in rom
|
||
|
;
|
||
|
MOV AX,0F000h
|
||
|
MOV ES,AX
|
||
|
CMP BYTE PTR ES:[0FFFEh],0FDh
|
||
|
JZ SCAN_IT
|
||
|
NO_ROM:
|
||
|
CLC
|
||
|
ROM_RET:
|
||
|
POP BX
|
||
|
POP AX
|
||
|
POP CX
|
||
|
POP DI
|
||
|
POP SI
|
||
|
POP ES
|
||
|
RET
|
||
|
SCAN_IT:
|
||
|
;
|
||
|
; start scanning at C000
|
||
|
;
|
||
|
MOV AX,0C000h
|
||
|
SCAN_ONE:
|
||
|
MOV ES,AX
|
||
|
XOR DI,DI
|
||
|
SCAN_MODULE:
|
||
|
;
|
||
|
; check for a valid header
|
||
|
;
|
||
|
CMP WORD PTR ES:[DI],0AA55h
|
||
|
JZ SCAN_LIST
|
||
|
ADD AX,080h
|
||
|
SCAN_END:
|
||
|
CMP AX,0F000h
|
||
|
JB SCAN_ONE
|
||
|
JMP NO_ROM
|
||
|
;
|
||
|
; trundle down list of names
|
||
|
;
|
||
|
SCAN_LIST:
|
||
|
MOV BL,ES:[DI].rom_length ; number of 512-byte jobbers
|
||
|
XOR BH,BH ; nothing in the high byte
|
||
|
SHL BX,1
|
||
|
SHL BX,1 ; number of paragraphs
|
||
|
ADD BX,7Fh
|
||
|
AND BX,0FF80h ; round to 2k
|
||
|
|
||
|
MOV DI,name_list
|
||
|
SCAN_NAME:
|
||
|
MOV CL,ES:[DI] ; length of name
|
||
|
INC DI ; point to name
|
||
|
XOR CH,CH
|
||
|
OR CX,CX ; zero length name
|
||
|
JNZ SCAN_TEST ; nope... compare
|
||
|
ADD AX,BX ; yep, skip to next block
|
||
|
JMP SCAN_END
|
||
|
;
|
||
|
; compare a single name
|
||
|
;
|
||
|
SCAN_TEST:
|
||
|
MOV SI,DX
|
||
|
INC SI
|
||
|
REPE CMPSB ; compare name
|
||
|
JZ SCAN_FOUND ; success!
|
||
|
SCAN_NEXT:
|
||
|
ADD DI,CX ; failure, next name piece
|
||
|
ADD DI,3
|
||
|
JMP SCAN_NAME
|
||
|
;
|
||
|
; found a name. save entry location
|
||
|
;
|
||
|
SCAN_FOUND:
|
||
|
CMP BYTE PTR DS:[SI],'?'
|
||
|
JZ SCAN_SAVE
|
||
|
CMP BYTE PTR DS:[SI],' '
|
||
|
JNZ SCAN_NEXT
|
||
|
SCAN_SAVE:
|
||
|
MOV [rom_cs],ES
|
||
|
MOV [ROM_ip],DI
|
||
|
STC
|
||
|
JMP ROM_RET
|
||
|
|
||
|
;
|
||
|
; execute a rom-placed body of code. allocate largest block
|
||
|
;
|
||
|
ROM_EXEC:
|
||
|
MOV BX,0FFFFh
|
||
|
MOV AH,ALLOC
|
||
|
INT int_command
|
||
|
MOV AH,ALLOC
|
||
|
INT int_command
|
||
|
PUSH BX
|
||
|
PUSH AX
|
||
|
;
|
||
|
; set terminate addresses
|
||
|
;
|
||
|
MOV AX,(set_interrupt_vector SHL 8) + int_terminate
|
||
|
PUSH DS
|
||
|
MOV DS,[RESSEG]
|
||
|
ASSUME DS:RESGROUP
|
||
|
MOV DX,OFFSET RESGROUP:EXEC_WAIT
|
||
|
INT int_command
|
||
|
MOV DX,DS
|
||
|
MOV ES,DX
|
||
|
ASSUME ES:RESGROUP
|
||
|
POP DS
|
||
|
ASSUME DS:NOTHING
|
||
|
;
|
||
|
; and create program header and dup all jfn's
|
||
|
;
|
||
|
POP DX
|
||
|
MOV AH,DUP_PDB
|
||
|
INT int_command
|
||
|
;
|
||
|
; set up dma address
|
||
|
;
|
||
|
MOV DS,DX
|
||
|
MOV DX,080h
|
||
|
MOV AH,SET_DMA
|
||
|
INT int_command
|
||
|
;
|
||
|
; copy in environment info
|
||
|
;
|
||
|
MOV AX,[ENVIRSEG]
|
||
|
MOV DS:[PDB_environ],AX
|
||
|
;
|
||
|
; set up correct size of block
|
||
|
;
|
||
|
POP BX ; BX has size, DS has segment
|
||
|
MOV DX,DS
|
||
|
ADD DX,BX
|
||
|
MOV DS:[PDB_block_len],DX
|
||
|
;
|
||
|
; change ownership of block
|
||
|
;
|
||
|
MOV DX,DS
|
||
|
DEC DX
|
||
|
MOV DS,DX
|
||
|
INC DX
|
||
|
MOV DS:[arena_owner],DX
|
||
|
MOV DS,DX
|
||
|
;
|
||
|
; set up correct stack
|
||
|
;
|
||
|
CMP BX,1000h
|
||
|
JB GOT_STACK
|
||
|
XOR BX,BX
|
||
|
GOT_STACK:
|
||
|
MOV CL,4
|
||
|
SHL BX,CL
|
||
|
MOV DX,DS
|
||
|
MOV SS,DX
|
||
|
MOV SP,BX
|
||
|
XOR AX,AX
|
||
|
PUSH AX
|
||
|
;
|
||
|
; set up initial registers and go to the guy
|
||
|
;
|
||
|
NOT AX
|
||
|
PUSH [ROM_CS]
|
||
|
PUSH [ROM_IP]
|
||
|
MOV ES,DX
|
||
|
ASSUME ES:NOTHING
|
||
|
FOOBAR PROC FAR
|
||
|
RET
|
||
|
FOOBAR ENDP
|