Revert "Throw out previous x86 stuff"

This reverts commit 807db4a592.
This commit is contained in:
Nero 2020-03-01 12:03:35 +00:00
parent 30ae244ef1
commit 4847eb4d88
24 changed files with 2507 additions and 0 deletions

View File

@ -6,6 +6,9 @@ cp437.bin: src/cp437.bmp scripts/bmp2font
vga11.bin: src/vga11.asm cp437.bin
nasm -o $@ $<
forth.bin: src/forth.asm
nasm -o $@ $<
scripts/%: src/scripts/%.c
mkdir -p scripts
gcc -o $@ $<

234
bdos/8086.asm Normal file
View File

@ -0,0 +1,234 @@
ORG BDOS
%include "hdr/bios.asm"
%include "hdr/fcb.asm"
%include "hdr/bpb.asm"
DISKBUF: EQU (BDOS-0x200) ; deblocking
DISKBPB: EQU (DISKBUF-21) ; BPB of the current driv
DISKDRV: EQU (DISKBPB-1) ; BYTE denoting drive of current fcb (1=A, ...)
PROGBX: EQU (DISKDRV-2)
STACK: EQU (PROGBX & 0xFFFE) ; even address, grows down
DEFDRV: EQU 4 ; default drive when opening a FCB (1=A, ...)
SYSCALL:
MOV [PROGBX], BX
XOR BH, BH
MOV BL, CL
ADD BX, BX
ADD BX, FUNCS
MOV BX, [BX]
PUSH BX
MOV BX, [PROGBX]
SYSRET:
RET
FUNCS:
DW SETUP,GETC,PUTC,SYSRET
DW SYSRET,SYSRET,SYSRET,SYSRET
DW SYSRET,PUTS,GETS,STATUS
DW SYSRET,DISKRST,SETDEFDSK,FCBOPEN
DW SYSRET,SYSRET,SYSRET,SYSRET
DW SYSRET,SYSRET,SYSRET,SYSRET
SETUP:
MOV SP, STACK
MOV BYTE [DEFDRV], 0x01
MOV CX, DISKBUF
CALL SETDMA
SUB SP, 0x20
MOV BX, SP
CALL FCBOPEN
CLI
HALT:
HLT
JMP HALT
GETC:
CALL CONIN
TEST AL, AL
JZ GETC
PUSH DX
MOV DL, AL
CALL CONOUT
POP DX
ret
PUTC:
JMP CONOUT
PUTS:
PUSH SI
MOV SI, DX
PUTS_L:
MOV DL, BYTE [SI]
CMP DL, '$'
JZ PUTS_E
CALL CONOUT
INC SI
JMP PUTS_L
PUTS_E:
POP SI
RET
; BX base ptr to buffer
; CH maximum BL
; CL minimum BL
GETS:
PUSH AX
PUSH CX
PUSH DX
PUSH BX
; BX is base pointer
MOV BX, DX
; CL is maximum, CH is current position
MOV CX, [BX]
XOR CH, CH
; BX starts at actual character area
ADD BX, 2
GETS_L:
; Read and maybe handle control chars
CALL GETC
CMP AL, 0x0D
JE GETS_E
CMP AL, 8
JE GETS_BS
; Store character
PUSH BX
ADD BL, CH
ADC BH, 0
MOV [BX], AL
POP BX
; Loop if we arent full yet
INC CH
CMP CH, CL
JC GETS_L
GETS_E:
; Append LF to CR
MOV DL, 0x0A
CALL PUTC
; Write back length data
SUB BX, 2
MOV [BX], CX
; Restore registers
POP BX
POP DX
POP CX
POP AX
ret
GETS_BS:
TEST CH, CH
JZ GETS_L
MOV DL, 0x20
CALL PUTC
MOV DL, 8
CALL PUTC
DEC CL
JMP GETS_L
STATUS:
JMP CONST
DISKRST:
MOV BYTE [DISKDRV], 0xFF
RET
SETDEFDSK:
MOV BYTE [DEFDRV], DL
RET
FCBOPEN:
MOV CL, BYTE [BX]
TEST CL, CL
JNZ NODEFDRV
MOV CL, BYTE [DEFDRV]
MOV BYTE [BX], CL
NODEFDRV:
CALL LOADBPB
MOV CX, [DISKBPB+BPB_RDE]
CALL FATSIZE
ADD DX, WORD [DISKBPB+BPB_RSC]
CALL SETLSEC
CALL READ
MOV AX, [DISKBUF]
INT3
; TODO: search for file in rootdir
; TODO: init cluster number
RET
; Set logical sector number
; IN DX sector number
SETLSEC:
PUSH AX
PUSH CX
PUSH DX
XOR AX, AX
XCHG AX, DX
DIV WORD [DISKBPB+BPB_SPT]
MOV CX, AX
CALL SETTRK
MOV CX, DX
INC CX
CALL SETSEC
POP DX
POP CX
POP AX
RET
; OUT DX number of sectors by all FATs
FATSIZE:
PUSH AX
MOV DL, BYTE [DISKBPB+BPB_FN]
MOV AL, BYTE [DISKBPB+BPB_SF]
MUL DL
XCHG AX, DX
POP AX
RET:
RET
; IN CL drive num, 1=A, 2=B, 3=C
LOADBPB:
CMP BYTE [DISKDRV], CL
JE RET
PUSH CX
DEC CL
CALL SELDSK
; first track
MOV CX, 0
CALL SETTRK
; first sector
MOV CX, 1
CALL SETSEC
; into default diskbuf
MOV CX, DISKBUF
CALL SETDMA
POP CX
CALL READ
JC DISKRST
; copy BPB
PUSH CX
PUSH SI
PUSH DI
MOV CX, 21
MOV SI, DISKBUF+0x0B
MOV DI, DISKBPB
REP MOVSB
POP DI
POP SI
POP CX
; store drive number
MOV BYTE [DISKDRV], CL
RET

85
bdos/dump.asm Normal file
View File

@ -0,0 +1,85 @@
; print a word
; in: ax
print16:
xchg ah,al
call print8
xchg ah,al
; print a byte
; in: al
print8:
push ax ; avoid destroying ah
push bx
xor bx, bx
aam 16 ; high nibble moved into ah, low nibble into al
add ax, 0x3030
push ax
xchg al, ah
call .nib
pop ax
call .nib
pop bx
pop ax
ret
.nib:
cmp al, 0x3a
jl .out
add al, 0x07
.out:
mov dl, al
call CONOUT
ret
dump:
push ax
push cx
push dx
push bx
mov dl, 0x0A
call CONOUT
mov dl, 0x0D
call CONOUT
mov cx, 4
.loop_line:
push cx
push bx
mov ax, bx
call print16
mov dl, ':'
call CONOUT
mov dl, ' '
call CONOUT
mov cx, 0x8
.loop_bin:
mov ax, [bx]
xchg al, ah
inc bx
inc bx
call print16
mov dl, ' '
call CONOUT
loop .loop_bin
pop bx
mov cx, 0x10
.loop_ascii:
mov dl, '.'
cmp byte [bx], 0x20
jc .print
cmp byte [bx], 0x80
jnc .print
mov dl, [bx]
.print:
inc bx
call CONOUT
loop .loop_ascii
pop cx
mov dl, 0x0A
call CONOUT
mov dl, 0x0D
call CONOUT
loop .loop_line
pop bx
pop dx
pop cx
pop ax
ret

218
bios/8086.asm Normal file
View File

@ -0,0 +1,218 @@
ORG BIOS
CPU 8086
DISKCX: EQU 0x8
DISKDX: EQU 0xA
DISKBX: EQU 0xC
JMP NEAR BOOT
JMP NEAR WBOOT
JMP NEAR CONST
JMP NEAR CONIN
JMP NEAR CONOUT
JMP NEAR LIST
JMP NEAR PUNCH
JMP NEAR READER
JMP NEAR HOME
JMP NEAR SELDSK
JMP NEAR SETTRK
JMP NEAR SETSEC
JMP NEAR SETDMA
JMP NEAR READ
JMP NEAR WRITE
JMP NEAR LISTST
BOOT:
MOV AL, 0x86
CALL MSG
DB "BIOS", 0
WBOOT:
XOR CX, CX
JMP BDOS
RET
; CHAR I/O
CONST:
MOV AH, 1
INT 0x16
JZ .END
MOV AL, 0xFF
RET
.END:
XOR AL, AL
RET
CONIN:
XOR AX, AX
INT 0x16
TEST AL, AL
JZ CONIN
RET
MSG:
POP SI
.LOOP:
MOV DL, BYTE [SI]
INC SI
TEST DL, DL
JZ .END
CALL CONOUT
JMP .LOOP
.END:
MOV DL, 0x20
CALL CONOUT
CALL PRINTAL
MOV DL, 0x0A
CALL CONOUT
MOV DL, 0x0D
CALL CONOUT
PUSH SI
RET
PRINTAL:
PUSH AX
PUSH DX
AAM 16
MOV DL, AH
CALL PRINTNIB
MOV DL, AL
CALL PRINTNIB
POP DX
POP AX
RET
PRINTNIB:
ADD DL, 0x30
CMP DL, 0x3a
JL CONOUT
ADD DL, 0x07
CONOUT:
PUSH AX
PUSH BX
MOV AH, 0x0E
MOV AL, DL
XOR BX, BX
INT 0x10
POP BX
POP AX
RET
LIST:
PUSH DX
XOR AH, AH
MOV AL, DL
INT 0x17
POP DX
RET
LISTST:
STC
RET
PUNCH:
STC
RET
READER:
STC
RET
; DISK I/O
HOME:
MOV WORD [DISKCX], 0x0001
MOV BYTE [DISKDX+1], 0x00
RET
SELDSK:
MOV BYTE [DISKDX], CL
XOR BX, BX
RET
SETTRK:
PUSH CX
PUSH DX
MOV DX, CX
; CH for headnum, CL for SAL
MOV CX, 0x0006
; Take off LSB as head number
XOR CH, CH
RCR DX, 1
RCL CH, 1
MOV BYTE [DISKDX+1], CH
; DX is sector number now
; Shift into format for int13
; DX ------9876543210
SAL DX, CL
; DX 9876543210------
ROL DX, 1
ROL DX, 1
; DX 76543210------98
SAL DL, CL
; DX 7654321098------
AND WORD [DISKCX], 0x003F
OR WORD [DISKCX], DX
POP DX
POP CX
RET
SETSEC:
AND WORD [DISKCX], 0xFFC0
OR BYTE [DISKCX], CL
RET
SETDMA:
MOV WORD [DISKBX], CX
RET
READ:
PUSH CX
PUSH DX
PUSH BX
MOV AX, 0x0201
MOV CX, [DISKCX]
MOV DX, [DISKDX]
MOV BX, [DISKBX]
INT 0x13
XCHG AH, AL
POP BX
POP DX
POP CX
JC DISKERR
RET
WRITE:
PUSH CX
PUSH DX
PUSH BX
MOV AX, 0x0301
MOV CX, [DISKCX]
MOV DX, [DISKDX]
MOV BX, [DISKBX]
INT 0x13
XCHG AH, AL
POP BX
POP DX
POP CX
JC DISKERR
RET
DISKERR:
PUSHF
PUSH SI
CALL MSG
DB "DISKERR", 0
POP SI
POPF
RET

241
boot/fat.asm Normal file
View File

@ -0,0 +1,241 @@
; Memory layout:
%define segment 0x00100
%define self (0x7C00-(segment<<4)) ; 1 sector
%define fattab (self+0x200) ; variable size
%define rootdir 0x00100 ; variable size
%define prog 0x0F000 ; 4K at the end for OS
org self
jmp short init
cluster_offset:
dw 0
times (0x0B - ($-$$)) db 0
fdc:
.ss:
dw 0x200 ; sector size
.sc:
db 2 ; sectors per cluster
.rsc:
dw 1 ; reserved sector count
.fn:
db 2 ; number of file allocation tables
.rde:
dw 0x70 ; number of root directory entries
.ts:
dw 720 ; total number of sectors
.mi: ; medium identifier
db 0xFD ; 5.25-inch Double sided, 40 tracks per side, 9 sectors per track (360 KB)
.sf: ; sectors per fat
dw 2
.spt:
dw 9 ; sectors per track
.nos:
dw 2 ; number of sides (heads)
.po:
dd 0 ; partition offset (in LBA blocks)
.lrgts:
dd 0
.drv:
db 0 ; drive number
db 0
db 0x29 ; efdc signature
.vid:
dd 0 ; volume id
.vlabel:
db "2B"
times (54 - ($-$$)) db " "
.fstype:
db "FAT12"
times (62 - ($-$$)) db " "
; mformat keeps writing until here
; if we place init earlier, code gets overwritten
times (62 - ($-$$)) nop
init:
cli
jmp segment:main
main:
; Stack grows down from 64k
mov ax, cs
mov ss, ax
mov sp, prog
mov ds, ax
mov es, ax
mov [fdc.drv], dl ; save drive number in fd
sti
; load fat table into memory
mov ax, [fdc.rsc]
mov cx, [fdc.sf]
xor dx, dx
mov bx, fattab
call load_sectors
; calculate length of rootdir
mov ax, [fdc.rde]
mov cl, 4
shr ax, cl ; 32 bytes per entry
mov cx, ax
; load root dir
xor dx, dx
mov ax, [fdc.sf]
mul byte [fdc.fn]
add ax, [fdc.rsc]
mov bx, rootdir
call load_sectors
; remember where we left off
; clusters start after rootdir
mov [cluster_offset], ax
; Load kernel
mov bx, prog
mov ax, kernel_name
call load_file
mov bp, 0x3332
jc error
mov dl, [fdc.drv]
; jump into kernel
jmp segment:prog
; Load a file into memory
; IN AX pointer to 8.3 filename
; ES:BX pointer to target area
; OUT CF flag set if error
; DI file size in bytes (<64K)
load_file:
mov si, rootdir
mov cx, [fdc.rde]
.search:
push cx
push si
mov di, ax
mov cx, 11
repe cmpsb
pop si
pop cx
je .read
add si, 0x20
loop .search
xor di, di
stc
ret
.read:
mov ax, [si+0x1A]
mov di, [si+0x1C]
jmp read_clusters
; Read the file given by cluster number
; into the target program area
; in ax cluster number
read_clusters:
; read cluster into area for target file
push ax
sub ax, 2
mul BYTE [fdc.sc]
add ax, [cluster_offset]
xor dx, dx
mov cx, [fdc.sc]
xor ch, ch
call load_sectors
pop ax
; calculate index in FAT
mov si, ax
shr si, 1
add si, ax
add si, fattab
; load entry from FAT, truncate to 12 bit
mov dx, [si]
test ax, 1
jz .noshift
mov cl, 4
shr dx, 4
.noshift:
mov ax, dx
and ax, 0x0FFF
cmp ax, 0x0FF8
jc read_clusters
ret
; Read sectors from disk
; Does not return on error
; ax and bx will be incremented, cx decremented
; in dx:ax sector number
; es:bx buffer
; cx number of sectors to read
; out dx:ax next sector to read
; es:bx next free buffer
; cx zero
load_sectors:
; fail instantly if reading sectors > 16 bit
test dx, dx
mov bp, 0x3330
jnz error
.loop:
push ax
push cx
push dx
; add partition offset (required for HDD)
add ax, [fdc.po]
adc dx, [fdc.po+2]
; calculate CHS data
div word [cs:fdc.spt] ; ax:temp = (lba / spt)
inc dx ; dx:sector = (lba % spt) + 1
mov cl, dl ; sector number
xor dx, dx
div word [cs:fdc.nos] ; ax:cylinder = (tmp / heads)
; dx:head = (tmp % heads)
mov ch, al ; cylinder number
mov dh, dl ; head number
mov dl, [cs:fdc.drv] ; driver number
mov ax, 0x0201 ; ah=0x02 al=0x01
int 0x13
mov bp, 0x3331
jc error
pop dx
pop cx
pop ax
; count up for next sector
add bx, 0x0200
inc ax
loop .loop
ret
error:
mov ax, bp
mov ah, 0x0e
mov bx, 7
int 0x10
mov al, 0x21
int 0x10
xor ax, ax
int 0x16
int 0x19
kernel_name:
db "BDOS BIN"
times (0x1FE - ($-$$)) db 0
dw 0xAA55

136
boot/mbr.asm Normal file
View File

@ -0,0 +1,136 @@
; Memory layout:
%define self 0x00600 ; 1 sector
%define prog 0x07C00 ; 1 sector
; FDC fields in VBR
%define spt (prog + 0x18)
%define nos (prog + 0x1A)
%define po (prog + 0x1C)
org self
cpu 8086
init:
cli
; Stack grows down from PSP + 64k
xor ax, ax
mov ss, ax
mov sp, ax
push dx
; Relocate from [prog] to [self]
mov ds, ax
mov es, ax
mov si, prog
mov di, self
mov cx, 0x100
rep movsw
jmp 0:main
main:
mov bp, 0x3335
mov si, part1
mov cx, 4
.loop:
test BYTE [si], 0x80
jnz loadpart
add si, 0x10
loop .loop
jmp error
loadpart:
; transfer starting address into DAP
push si
add si, 0x08
mov di, dap.blocknum
movsw
movsw
pop si
; load sector
push si
mov si, dap
mov bp, 0x3336
mov ah, 0x42
stc
int 0x13
jc error
pop si
cmp BYTE [si+4], 0x01
jne jump
adjust:
push dx
mov bp, 0x3337
mov ah, 0x08
stc
int 0x13
jc error
; update sectors per track
xor ax, ax
mov al, cl
mov [spt], ax
; update number of sides
xor ax, ax
mov al, dh
mov [nos], ax
; update partition offset
push si
add si, 0x08
mov di, po
movsw
movsw
pop si
pop dx
jump:
jmp 0:prog
error:
mov ax, bp
mov ah, 0x0e
mov bx, 7
int 0x10
mov al, 0x21
int 0x10
xor ax, ax
int 0x16
int 0x19
dap:
.size:
db 0
db 0
.count:
dw 1
.buffer:
dw prog
dw 0
.blocknum:
dq 0
times (0x1BE - ($-$$)) db 0
part1:
db 0x80
.chs_start:
db 0xFF, 0xFF, 0xFF
.type:
db 0x01
.chs_end:
db 0xFF, 0xFF, 0xFF
.begin:
dd 1
.end:
dd (FLOPPY * 2)
times (0x1FE - ($-$$)) db 0
dw 0xAA55

90
boot/serial.asm Normal file
View File

@ -0,0 +1,90 @@
org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
jmp 0:start
getc:
mov ah, 0x02
mov dx, 0x0000
int 0x14
test ah, 0x80
jnz getc
ret
putc:
mov ah, 0x01
mov dx, 0x0000
int 0x14
ret
init_port:
mov ah, 0x00
mov al, 0b11100011
mov dx, 0x0000
int 0x14
ret
dump:
mov si, 0x500
mov cx, di
sub cx, si
.loop:
lodsb
call print8
loop .loop
mov al, 0x0A
call putc
mov al, 0x0D
call putc
ret
line_reset:
cmp di, 0x500
je .reset
call line_process
.reset:
mov di, 0x500
xor bp, bp
jmp mainloop
line_process:
call dump
ret
start:
call init_port
mov di, 0x500
jmp line_reset
mainloop:
call getc
cmp al, ':'
je line_reset
cmp al, 0x20
jb line_reset
sub al, 0x30
cmp al, 9
jbe .noadjust
sub al, 7
.noadjust:
test bp, bp
jnz .secondnib
mov cl, 4
shl al, cl
mov [di], al
not bp
jmp mainloop
.secondnib:
or [di], al
inc di
not bp
jmp mainloop
%include "print.asm"
times 510-($-$$) db 0x00
db 0x55,0xaa

21
com/asm86.asm Normal file
View File

@ -0,0 +1,21 @@
org 0x100
jmp start
%include "fcbparse.asm"
fcb_asm:
times 36 db 0
fcb_bin:
times 36 db 0
fcb_lst:
times 36 db 0
start:
mov si, 0x81
mov bx, fcb_asm
mov ax, 0x1234
call fcb_parse
ret

17
com/hello.asm Normal file
View File

@ -0,0 +1,17 @@
org 0x0100
main:
mov si, hello
.loop:
lodsb
test al, al
jz .ret
mov dl, al
mov cl, 0x02
call 5
jmp .loop
.ret:
ret
hello:
db "Hello!", 0x0A, 0x0D, 0

17
hdr/bios.asm Normal file
View File

@ -0,0 +1,17 @@
; Labels for BIOS entry points
BOOT: EQU (BIOS+0)
WBOOT: EQU (BIOS+3)
CONST: EQU (BIOS+6)
CONIN: EQU (BIOS+9)
CONOUT: EQU (BIOS+12)
LIST: EQU (BIOS+15)
PUNCH: EQU (BIOS+18)
READER: EQU (BIOS+21)
HOME: EQU (BIOS+24)
SELDSK: EQU (BIOS+27)
SETTRK: EQU (BIOS+30)
SETSEC: EQU (BIOS+33)
SETDMA: EQU (BIOS+36)
READ: EQU (BIOS+39)
WRITE: EQU (BIOS+42)
LISTST: EQU (BIOS+45)

12
hdr/bpb.asm Normal file
View File

@ -0,0 +1,12 @@
; BPB from first sector on FAT fs, starting 0x0B
BPB_SS: EQU 0 ; WORD sector size
BPB_SC: EQU 2 ; BYTE sectors per cluster
BPB_RSC: EQU 3 ; WORD reserved sector count
BPB_FN: EQU 5 ; BYTE number of FATs
BPB_RDE: EQU 6 ; WORD number of root directory entries
BPB_TS: EQU 8 ; WORD total number of sectors
BPB_MI: EQU 10 ; BYTE medium identifier
BPB_SF: EQU 11 ; WORD sectors per FAT
BPB_SPT: EQU 13 ; WORD sectors per track
BPB_NOS: EQU 15 ; WORD number of sides/heads
BPB_PO: EQU 17 ; DWORD partition offset

10
hdr/fcb.asm Normal file
View File

@ -0,0 +1,10 @@
; File control block
FCB_DRV: EQU 0 ; BYTE 0=A: 1=B: 2=C: ...
FCB_NAM: EQU 1 ; 8 BYTES, space padded
FCB_EXT: EQU 9 ; 3 BYTES, space padded
FCB_TYP: EQU 12 ; BYTE FCB type
; TYP=1: regular file
FCB_BLK: EQU 13 ; current 128b block in sector
FCB_CLU: EQU 14 ; current sector
FCB_LFT: EQU 16 ; bytes left to read in current file
FCB_END: EQU 20 ; FCB length

65
lib/earlymcb.asm Normal file
View File

@ -0,0 +1,65 @@
%define mcb_first 0x5F
; This alloc uses an MCB-like list always starting at a fixed offset.
; Boot components may use it to allocate space during early boot.
; The list ends with the most recently allocated arena at its end,
; contrary to how the MCBs work on DOS.
; Memory Layout:
; 0000 Interrupt table
; 0400 BIOS data area
; 05F0 16 bytes Arena header 1
; 0600 BX paragraphs Arena 1
; .... 16 bytes Arena header 2
; .... BX paragraphs Arena 2
; ...
; .... 16 bytes Arena header n
; .... BX paragraphs Arena n
; Unmanaged memory
; Take note that this mechanism works differently than under DOS -
; but this one is optimized for fast and small alloc.
; Typical usecases:
; - Diskette Parameter Table (See interrupt 1Eh)
; - Partition table
; - Boot sector relocations
; - Boot sector data (FAT table, rootdir)
; Allocate paragraphs in unmanaged memory
; IN BX requested memory in paragraphs
; OUT AX output segment
early_alloc:
push ds
push bx
push si
; si is used as zero register
; this allows the opcodes to use a byte as disp instead of word
xor si, si
mov ax, mcb_first
.next:
mov ds, ax
inc ax
; DS is seg of arena header
; AX is seg of arena
cmp BYTE [si], 0x4D
jne .here
add ax, WORD [si+3]
jmp .next
.here:
; mark as item
mov BYTE [si], 0x4D
; write segment length in paragraphs
mov WORD [si+3], bx
; if allocation is higher than CS, then we are in managed memory
; set ourself as owner then, otherwise use dummy value 8
mov bx, cs
cmp ax, bx ; CF = CS in unmanaged memory
jnc .setup_owner
mov bx, 8
.setup_owner:
mov WORD [si+1], bx
pop si
pop bx
pop ds
ret

46
lib/fcbparse.asm Normal file
View File

@ -0,0 +1,46 @@
; Parse ASCIIZ string into FCB
; IN SI ptr to filename
; BX ptr to FCB
fcb_parse:
push di
push ax
mov di, bx
xor ax, ax
stosb
.cleanout:
push di
mov cx, 0x0A
mov al, 0x20
rep stosb
pop di
.base_loop:
call .read
cmp al, 0x2E
je .ext_start
cmp al, 0x20
je .ret
stosb
jmp .base_loop
.ext_start:
mov di, bx
add di, 9
.ext_loop:
call .read
cmp al, 0x20
je .ret
stosb
jmp .ext_loop
.read:
lodsb
test al, al
jz .eret
cmp al, 0x0D
je .eret
ret
.eret:
dec si
pop ax
.ret:
pop ax
pop di
ret

14
lib/fdc.asm Normal file
View File

@ -0,0 +1,14 @@
; FDC Descriptor / BIOS parameter block
fdc_ss: equ 0x0B ; WORD sector size
fdc_sc: equ 0x0D ; BYTE sectors per cluster
fdc_rsc: equ 0x0E ; WORD reserved sectors
fdc_fn: equ 0x10 ; BYTE FAT tables
fdc_rde: equ 0x11 ; WORD root directory entries
fdc_ts: equ 0x13 ; WORD total sector count
; 0 if >65535, use lrgts then
fdc_mi: equ 0x15 ; BYTE media descriptor byte
fdc_sf: equ 0x16 ; WORD sectors per FAT
fdc_spt: equ 0x18 ; WORD sectors per track
fdc_nos: equ 0x1A ; WORD sides/heads
fdc_po: equ 0x1C ; DWORD partition offset
fdc_lrgts: equ 0x20 ; DWORD large sector count

15
lib/log2.asm Normal file
View File

@ -0,0 +1,15 @@
; Calculate dual logarithm (int)
; IN AX number
; OUT AX dual logarithm
log2:
push cx
mov cx, 15
.loop:
rcl ax, 1
jc .ret
loop .loop
.ret:
cmc
mov ax, cx
pop cx
ret

124
lib/nasmcomp.asm Normal file
View File

@ -0,0 +1,124 @@
; Pseudo-Instruction: fill bytes until offset
%macro pad 1.nolist
times (%1 - ($-$$)) nop
%endmacro
; REGISTERS
%define a al
%define b ch
%define c cl
%define d dh
%define e dl
%define h bh
%define l bl
%define m byte [bx]
; REGISTER PAIRS
%define bc cx
%define de dx
%define hl bx
; INSTRUCTIONS
; data movement
; 'mov' can stay literal
%define mvi mov
%define lxi mov
%macro lda 1
mov al, byte [%1]
%endmacro
%macro sta 1
mov byte [%1], al
%endmacro
%macro lhld 1
mov bx, word [%1]
%endmacro
%macro shld 1
mov word [%1], bx
%endmacro
%macro ldax 1
mov al, byte [%1]
%endmacro
%macro stax 1
mov byte [%1], al
%endmacro
%macro xchg 1
xchg dx, bx
%endmacro
; addition
%macro add 1
add al, %1
%endmacro
%macro adi 1
add al, %1
%endmacro
%macro adc 1
adc al, %1
%endmacro
%macro aci 1
adc al, %1
%endmacro
; subtraction
%macro sub 1
sub al, %1
%endmacro
%macro sui 1
sub al, %1
%endmacro
%macro sbb 1
sub al, %1
%endmacro
%macro sbi 1
sub al, %1
%endmacro
; increment / decrement
%define inr inc
%define dcr dec
%define inx inc
%define dcx dec
; pointer arithmetic
%macro dad 1
add bx, %1
%endmacro
; 'daa' stays literal
;ANA S 10100SSS ZSCPA AND register with A
;ANI # 11100110 db ZSPCA AND immediate with A
;ORA S 10110SSS ZSPCA OR register with A
;ORI # 11110110 ZSPCA OR immediate with A
;XRA S 10101SSS ZSPCA ExclusiveOR register with A
;XRI # 11101110 db ZSPCA ExclusiveOR immediate with A
;CMP S 10111SSS ZSPCA Compare register with A
;CPI # 11111110 ZSPCA Compare immediate with A
;RLC 00000111 C Rotate A left
;RRC 00001111 C Rotate A right
;RAL 00010111 C Rotate A left through carry
;RAR 00011111 C Rotate A right through carry
;CMA 00101111 - Compliment A
;CMC 00111111 C Compliment Carry flag
;STC 00110111 C Set Carry flag
;JMP a 11000011 lb hb - Unconditional jump
;Jccc a 11CCC010 lb hb - Conditional jump
;CALL a 11001101 lb hb - Unconditional subroutine call
;Cccc a 11CCC100 lb hb - Conditional subroutine call
;RET 11001001 - Unconditional return from subroutine
;Rccc 11CCC000 - Conditional return from subroutine
;RST n 11NNN111 - Restart (Call n*8)
;PCHL 11101001 - Jump to address in H:L
;PUSH RP 11RP0101 *2 - Push register pair on the stack
;POP RP 11RP0001 *2 *2 Pop register pair from the stack
;XTHL 11100011 - Swap H:L with top word on stack
;SPHL 11111001 - Set SP to content of H:L
;IN p 11011011 pa - Read input port into A
;OUT p 11010011 pa - Write A to output port
;EI 11111011 - Enable interrupts
;DI 11110011 - Disable interrupts
;HLT 01110110 - Halt processor
;NOP 00000000 - No operation
%macro cnz 1
jz %%skip
call near %1
%%skip:
%endmacro

142
lib/opcode80.asm Normal file
View File

@ -0,0 +1,142 @@
; BYTE name length
; n BYTES name
; BYTE opcode template (variable parts zero)
;
; BYTE operand 1
; BYTE operand 2
; operand types:
; 0x00 none
; 0x1n reg8, opcode |= (reg8 << n)
; 0x2n reg16, opcode |= (reg8 << n)
; 0x3n reg16b, opcode |= (reg8 << n)
; 0x7n 3-bit number, opcode |= (num << n)
; 0x80 imm8, encoded as extra byte
; 0x81 imm16, encoded as extra word
registers:
.reg8:
db "B", 0
db "C", 0
db "D", 0
db "E", 0
db "H", 0
db "L", 0
db "M", 0
db 0
.reg16:
db "BC", 0
db "DE", 0
db "HL", 0
db "SP", 0
db 0
.reg16b:
db "BC", 0
db "DE", 0
db "HL", 0
db "AF", 0
db 0
.end:
db 0
opcodes:
; Data movement
db 3, "MOV", 0b01000000, 0x13, 0x10
db 3, "MVI", 0b00000110, 0x13, 0x80
db 3, "LXI", 0b00000001, 0x24, 0x81
db 3, "LDA", 0b00111010, 0x81, 0x00
db 3, "STA", 0b00110010, 0x81, 0x00
db 4, "LHLD", 0b00101010, 0x81, 0x00
db 4, "SHLD", 0b00100010, 0x81, 0x00
db 4, "LDAX", 0b00001010, 0x24, 0x00
db 4, "STAX", 0b00000010, 0x24, 0x00
db 4, "XCHG", 0b11101011, 0x00, 0x00
; Addition / Subtraction
db 3, "ADD", 0b10000000, 0x10, 0x00
db 3, "ADI", 0b11000110, 0x80, 0x00
db 3, "ADC", 0b10001000, 0x10, 0x00
db 3, "ACI", 0b11001110, 0x80, 0x00
db 3, "SUB", 0b10010000, 0x10, 0x00
db 3, "SUI", 0b11010110, 0x80, 0x00
db 3, "SBB", 0b10011000, 0x10, 0x00
db 3, "SBI", 0b11011110, 0x80, 0x00
db 3, "INR", 0b00000100, 0x13, 0x00
db 3, "DCR", 0b00000101, 0x13, 0x00
db 3, "INX", 0b00000011, 0x24, 0x00
db 3, "DCX", 0b00001011, 0x24, 0x00
db 3, "DAD", 0b00001001, 0x24, 0x00
; BCD
db 3, "DAA", 0b00100111, 0x00, 0x00
; Bitwise operations
db 3, "ANA", 0b10100000, 0x10, 0x00
db 3, "ANI", 0b11100110, 0x80, 0x00
db 3, "ORA", 0b10110000, 0x10, 0x00
db 3, "ORI", 0b11110110, 0x80, 0x00
db 3, "XRA", 0b10101000, 0x10, 0x00
db 3, "XRI", 0b11101110, 0x80, 0x00
db 3, "CMP", 0b10111000, 0x10, 0x00
db 3, "CPI", 0b11111110, 0x80, 0x00
; Rotate
db 3, "RLC", 0b00000111, 0x00, 0x00
db 3, "RRC", 0b00001111, 0x00, 0x00
db 3, "RAL", 0b00010111, 0x00, 0x00
db 3, "RAR", 0b00011111, 0x00, 0x00
; Complement
db 3, "CMA", 0b00101111, 0x00, 0x00
db 3, "CMC", 0b00111111, 0x00, 0x00
db 3, "STC", 0b00110111, 0x00, 0x00
; Jump
db 3, "JMP", 0b11000011, 0x81, 0x00
db 3, "JNZ", 0b11000010, 0x81, 0x00
db 2, "JZ", 0b11001010, 0x81, 0x00
db 3, "JNC", 0b11010010, 0x81, 0x00
db 2, "JC", 0b11011010, 0x81, 0x00
db 3, "JPO", 0b11100010, 0x81, 0x00
db 3, "JPE", 0b11101010, 0x81, 0x00
db 2, "JP", 0b11110010, 0x81, 0x00
db 2, "JM", 0b11111010, 0x81, 0x00
; Call
db 4, "CALL", 0b11001101, 0x81, 0x00
db 3, "CNZ", 0b11000100, 0x81, 0x00
db 2, "CZ", 0b11001100, 0x81, 0x00
db 3, "CNC", 0b11010100, 0x81, 0x00
db 2, "CC", 0b11011100, 0x81, 0x00
db 3, "CPO", 0b11100100, 0x81, 0x00
db 3, "CPE", 0b11101100, 0x81, 0x00
db 2, "CP", 0b11110100, 0x81, 0x00
db 2, "CM", 0b11111100, 0x81, 0x00
; Return
db 3, "RET", 0b11001001, 0x00, 0x00
db 3, "RNZ", 0b11000000, 0x00, 0x00
db 2, "RZ", 0b11001000, 0x00, 0x00
db 3, "RNC", 0b11010000, 0x00, 0x00
db 2, "RC", 0b11011000, 0x00, 0x00
db 3, "RPO", 0b11100000, 0x00, 0x00
db 3, "RPE", 0b11101000, 0x00, 0x00
db 2, "RP", 0b11110000, 0x00, 0x00
db 2, "RM", 0b11111000, 0x00, 0x00
db 3, "RST", 0b11000111, 0x73, 0x00
db 4, "PCHL", 0b11101001, 0x00, 0x00
db 4, "PUSH", 0b11000101, 0x34, 0x00
db 3, "POP", 0b11000001, 0x34, 0x00
db 4, "XTHL", 0b11100011, 0x00, 0x00
db 4, "SPHL", 0b11111001, 0x00, 0x00
db 2, "IN", 0b11011011, 0x80, 0x00
db 3, "OUT", 0b11010011, 0x80, 0x00
db 2, "EI", 0b11111011, 0x00, 0x00
db 2, "DI", 0b11110011, 0x00, 0x00
db 3, "HLT", 0b01110110, 0x00, 0x00
db 3, "NOP", 0b00000000, 0x00, 0x00
db 0

30
lib/popcnt.asm Normal file
View File

@ -0,0 +1,30 @@
popcnt:
push bx
push cx
mov bx, ax
and ax, 0x5555 ; 8x 01
and bx, 0xaaaa ; 8x 10
shr bx, 1
add ax, bx
mov bx, ax
and ax, 0x3333 ; 4x 0011
and bx, 0xcccc ; 4x 1100
mov cl, 2
shr bx, cl
add ax, bx
mov bx, ax
and ax, 0x0f0f ; 2x 00001111
and bx, 0xf0f0 ; 2x 11110000
mov cl, 4
shr bx, cl
add ax, bx
add al, ah
xor ah, ah
pop cx
pop bx
ret

33
lib/print.asm Normal file
View File

@ -0,0 +1,33 @@
; important functions in this file: kprintf
; print a word
; in: ax
print16:
xchg ah,al
call print8
xchg ah,al
; print a byte
; in: al
print8:
push ax ; avoid destroying ah
push bx
xor bx, bx
aam 16 ; high nibble moved into ah, low nibble into al
add ax, 0x3030
push ax
xchg al, ah
call .nib
pop ax
call .nib
pop bx
pop ax
ret
.nib:
cmp al, 0x3a
jl .out
add al, 0x07
.out:
mov ah, 0x0e
int 0x10
ret

105
lib/printf.asm Normal file
View File

@ -0,0 +1,105 @@
printf:
cld
pop si
push bp
mov bp, sp
.loop:
mov al, [cs:si]
inc si
cmp al, 0x00
je .end
cmp al, 0x25
je .handle_25h
.literal:
call putc
jmp .loop
.end:
pop bp
push si
ret
.handle_25h:
mov al, [cs:si]
inc si
cmp al, 0x25
je .literal
cmp al, 0x58 ; 'X'
je .printhex
cmp al, 0x55 ; 'U'
je .printdec
cmp al, 0x53 ; 'S'
je .printstr
mov al, 0x3F
jmp .literal
.printhex:
add bp, 2
mov ax, [bp]
mov bx, 0x0010
call print_number
jmp .loop
.printdec:
add bp, 2
mov ax, [bp]
mov bx, 0x000A
call print_number
jmp .loop
.printstr:
add bp, 2
mov ax, [bp]
call print_string
jmp .loop
; converts a integer to ascii
; in ax input integer
; bx base
; cx minimum number of digits
; out bx garbled
; dx garbled
print_number_padded:
xor dx, dx
div bx
push dx
dec cx
jz .nopad
call print_number_padded
jmp print_number.end
.nopad:
call print_number
jmp print_number.end
; converts a integer to ascii
; in ax input integer
; bx base
; out bx garbled
; dx garbled
print_number:
xor dx, dx
div bx ; ax = dx:ax / 10, dx = dx:ax % 10
push dx
and ax, ax
jz .end
call print_number
.end:
pop bx
xor bh, bh
add bx, print_chars
mov al, [cs:bx]
call putc
ret
; putc's a string
; in DS:AX null-terminated string
print_string:
push si
mov si, ax
.loop:
lodsb
cmp al, 0x00
je .end
call putc
jmp .loop
.end:
pop si
ret
print_chars:
db "0123456789ABCDEF"

294
rom/debug.asm Normal file
View File

@ -0,0 +1,294 @@
cpu 8086
org 0x0000
%macro push8086 0
push ss
push es
push ds
push di
push si
push bp
push sp
push bx
push dx
push cx
push ax
; adjust stored SP to be value before interrupt
mov bp, sp
mov [bp+08], bp
add WORD [bp+08], 28
%endmacro
%macro pop8086 0
pop ax
pop cx
pop dx
pop bx
add sp, 2 ; skip SP
pop bp
pop si
pop di
pop ds
pop es
add sp, 2 ; skip SS
%endmacro
rom:
db 0x55, 0xAA
.sectors:
db 0x00
.init:
jmp init
nop
.name:
db "RDOS DEBUG", 0
times (0x18 - ($-$$)) db 0
.pcir_ptr:
dw 0
times (0x1A - ($-$$)) db 0
.pnp_ptr:
dw pnp
pnp:
db "$PnP"
.version:
db 1 ; version 1
.length:
db 2 ; 2 * 16 length
dw 0 ; offset of next header
db 0
.checksum:
db 0 ; checksum (filled by fix-rom)
dd 0 ; device identifier
dw 0 ; manufacturer string
dw rom.name ; product name string
db 0,0,0 ; device type string
db 0x20 ; device indicator, bit for "read cacheable" set
dw 0 ; boot connection vector
dw 0 ; boot disconnect vector
dw 0 ; bootstrap entry point
dw 0 ; reserved
dw 0
init:
mov bx, 0
mov dx, isr_divide_error
call hook_int
mov bx, 1
mov dx, isr_singlestep
call hook_int
mov bx, 2
mov dx, isr_nmi
call hook_int
mov bx, 3
mov dx, isr_breakpoint
call hook_int
mov bx, 4
mov dx, isr_overflow
call hook_int
; 5 left out
mov bx, 6
mov dx, isr_invalid_opcode
call hook_int
push cs
pop ds
mov si, rom.name
push cs
push si
call printf
db "%S %X", 0x0A, 0x0D, 0
add sp, 4
retf
; Hook interrupt
hook_int:
; bx times 4
add bx, bx
add bx, bx
; store offset
mov [bx], dx
; store segment
push ax
mov ax, cs
mov [bx+2], ax
pop ax
ret
putc:
push bx
push cx
mov ah, 0x0e
mov bx, 0x0000
int 0x10
pop cx
pop bx
ret
; Names for words in debug frame
; Two characters per word, 14 words total
debug_frame_names:
; general-purpose registers
db "AXCXDXBXSPBPSIDI"
; extra registers
db "DSESSSIPCSFL"
; Names for bits in debug_frame+26 (FL/Flags register)
; One character per bit, 16 bits total
debug_frame_flags:
db "++++ODIT"
db "SZ+A+P+C"
; Print a single register from the frame
; in SI frame offset for register
debug_frame_register_print:
mov bx, debug_frame_names
mov al, [cs:bx+si] ; first name char load
call putc
mov al, [cs:bx+si+1] ; second name char load
call putc
mov al, '='
call putc
mov ax, [ss:bp+si] ; value load
; prepare call to print_number
push bx
push cx
mov bx, 0x0010
mov cx, 3
call print_number_padded
pop cx
pop bx
mov al, ' '
call putc
ret
debug_frame_print:
mov si, 0
mov cx, 8
.reg1loop:
call debug_frame_register_print
add si, 2
loop .reg1loop
mov dx, [ss:bp+26]
mov di, debug_frame_flags
mov cx, 0x0010
.flag_loop:
mov al, [cs:di]
inc di
cmp al, '+'
je .next
test dx, 0x8000
jnz .write
mov al, '-'
.write:
call putc
.next:
sal dx, 1
loop .flag_loop
call printf
db 0x0A, 0x0D, 0
mov si, 16
mov cx, 5
.reg2loop:
call debug_frame_register_print
add si, 2
loop .reg2loop
call printf
db 0x0A, 0x0D, 0
ret
isr_divide_error:
push8086
call printf
db 0x0A, 0x0D, "INT 0 - DIVIDE ERROR", 0x0A, 0x0D, 0
call debug_frame_print
jmp halt
isr_singlestep:
push8086
call printf
db 0x0A, 0x0D, 0
call debug_frame_print
; wait for keypress
xor ax, ax
int 0x16
; enable trace flag so we fire again after next instruction
or word [ss:bp+26], 0x0100
pop8086
iret
isr_nmi:
push8086
call printf
db 0x0A, 0x0D, "INT 2 - NON-MASKABLE INTERRUPT", 0x0A, 0x0D, 0
call debug_frame_print
jmp halt
isr_breakpoint:
push8086
call printf
db 0x0A, 0x0D, 0
call debug_frame_print
pop8086
iret
isr_overflow:
push8086
call printf
db 0x0A, 0x0D, "INT 4 - OVERFLOW", 0x0A, 0x0D, 0
call debug_frame_print
jmp halt
isr_invalid_opcode:
push8086
call printf
db 0x0A, 0x0D, "INT 6 - INVALID OPCODE", 0x0A, 0x0D, 0
call debug_frame_print
jmp halt
halt:
call printf
db "HALTED", 0x0A, 0x0D, 0
.loop:
hlt
jmp halt
%include "printf.asm"
align 512

518
src/utils/em8080.c Normal file
View File

@ -0,0 +1,518 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
uint8_t mem[64*1024];
typedef struct flagbits {
unsigned int c : 1;
unsigned int u1 : 1;
unsigned int p : 1;
unsigned int u3 : 1;
unsigned int a : 1;
unsigned int u5 : 1;
unsigned int z : 1;
unsigned int s : 1;
} flagbits;
typedef struct reg {
uint8_t flags;
uint8_t a;
uint8_t c;
uint8_t b;
uint8_t e;
uint8_t d;
uint8_t l;
uint8_t h;
} reg;
typedef struct regp {
uint16_t psw;
uint16_t bc;
uint16_t de;
uint16_t hl;
} regp;
union regset {
struct reg reg;
struct regp regp;
} regset;
#define A regset.reg.a
#define B regset.reg.b
#define C regset.reg.c
#define D regset.reg.d
#define E regset.reg.e
#define H regset.reg.h
#define L regset.reg.l
#define M mem[HL]
#define BC regset.regp.bc
#define DE regset.regp.de
#define HL regset.regp.hl
#define flags (*(struct flagbits*)&regset.reg.flags)
uint16_t IP = 0x100;
uint16_t SP = 0;
unsigned int tmp;
// Dump registers
void dump() {
printf("\n");
printf("A=%02X BC=%04X DE=%04X HL=%04X M=%02X SP=%04X ", A, BC, DE, HL, M, SP);
printf("%c", flags.s ? 'S' : '-');
printf("%c", flags.z ? 'Z' : '-');
printf("%c", flags.a ? 'A' : '-');
printf("%c", flags.p ? 'P' : '-');
printf("%c", flags.c ? 'C' : '-');
printf("\n");
printf("IP=%04X : %02X %02X\n", IP, mem[IP], mem[IP+1]);
}
uint8_t imm8() {
uint8_t r = *(uint8_t*)&mem[IP];
IP++;
return r;
}
uint16_t imm16() {
uint16_t r = *(uint16_t*)&mem[IP];
IP+=2;
return r;
}
void push(uint16_t v) {
SP -= 2;
*(uint16_t*)&mem[SP] = v;
}
uint16_t pop() {
uint16_t v = *(uint16_t*)&mem[SP];
SP += 2;
return v;
}
void in(uint8_t port) {
}
void out(uint8_t port) {
}
int has_even_parity(uint8_t x){
unsigned int count = 0, i, b = 1;
for(i = 0; i < 8; i++){
if( x & (b << i) ){count++;}
}
if( (count % 2) ){return 0;}
return 1;
}
void cpm_syscall(int number) {
switch(number) {
case 0:
exit(0);
break;
case 2:
printf("%c", E);
break;
case 0x0C:
H=0x02;
L=0x00;
break;;
default:
fprintf(stderr, "Fatal: Unhandled CP/M syscall C=%02Xh\n", number);
exit(1);
}
}
void call(uint16_t v) {
switch(v) {
case 0:
exit(0);
break;;
case 5:
cpm_syscall(C);
break;;
default:
push(IP);
IP=v;
break;
}
}
void interrupt(int number) {
call(number << 3);
}
#define add16c(a, b) \
tmp = a + b; \
flags.c = (tmp >> 16) & 1
#define unarync(a, op) \
tmp=a; \
tmp op; \
flags.p = has_even_parity(tmp & 8); \
flags.z = !(tmp); \
flags.s = (tmp >> 7) & 1; \
a=tmp
#define aritht(a, op, b) \
tmp=a; \
tmp=a op b; \
flags.c = (tmp >> 8) & 1; \
flags.p = has_even_parity(tmp & 8); \
flags.z = !(tmp); \
flags.s = (tmp >> 7) & 1;
#define arith(a, op, b) \
aritht(a, op, b); \
a=tmp
// Execute a single instruction
void step() {
uint8_t opcode = mem[IP];
IP++;
switch(opcode) {
case 0x00: break;;
case 0x01: BC=imm16(); break;;
case 0x02: mem[BC]=A; break;;
case 0x03: BC++;
case 0x04: unarync(B, ++); break;;
case 0x05: unarync(B, --); break;;
case 0x06: B=imm8(); break;;
case 0x07: tmp=A; A = A << 1 | flags.c; flags.c = tmp >> 7; break;;
case 0x09: HL=add16c(HL,BC); break;;
case 0x0a: A=mem[BC]; break;;
case 0x0b: BC--;
case 0x0c: unarync(B, ++); break;;
case 0x0d: unarync(B, --); break;;
case 0x0e: C=imm8(); break;;
case 0x11: DE=imm16(); break;;
case 0x12: mem[DE]=A; break;;
case 0x13: DE++;
case 0x14: unarync(D, ++); break;;
case 0x15: unarync(D, --); break;;
case 0x16: D=imm8(); break;;
case 0x19: HL=add16c(HL,DE); break;;
case 0x1a: A=mem[DE]; break;;
case 0x1b: DE--;
case 0x1c: unarync(E, ++); break;;
case 0x1d: unarync(E, --); break;;
case 0x1e: E=imm8(); break;;
case 0x21: HL=imm16(); break;;
case 0x22: *(uint16_t*)&mem[imm16()]=HL; break;;
case 0x23: HL++;
case 0x24: unarync(H, ++); break;;
case 0x25: unarync(H, --); break;;
case 0x26: H=imm8(); break;;
case 0x29: HL=add16c(HL,HL); break;;
case 0x2a: HL=*(uint16_t*)&mem[imm16()]; break;;
case 0x2b: HL--;
case 0x2c: unarync(L, ++); break;;
case 0x2d: unarync(L, --); break;;
case 0x2e: L=imm8(); break;;
case 0x2f: A=~A; break;;
case 0x31: SP=imm16(); break;;
case 0x32: mem[imm16()]=A; break;;
case 0x33: SP++;
case 0x34: unarync(M, ++); break;;
case 0x35: unarync(M, --); break;;
case 0x36: M=imm8(); break;;
case 0x37: flags.c = 1; break;;
case 0x39: HL=add16c(HL,SP); break;;
case 0x3a: A=mem[imm16()]; break;;
case 0x3b: SP--;
case 0x3c: unarync(A, ++); break;;
case 0x3d: unarync(A, --); break;;
case 0x3e: A=imm8(); break;;
case 0x3f: flags.c = !flags.c; break;;
case 0x40: B=B; break;;
case 0x41: B=C; break;;
case 0x42: B=D; break;;
case 0x43: B=E; break;;
case 0x44: B=H; break;;
case 0x45: B=L; break;;
case 0x46: B=M; break;;
case 0x47: B=A; break;;
case 0x48: C=B; break;;
case 0x49: C=C; break;;
case 0x4a: C=D; break;;
case 0x4b: C=E; break;;
case 0x4c: C=H; break;;
case 0x4d: C=L; break;;
case 0x4e: C=M; break;;
case 0x4f: C=A; break;;
case 0x50: D=B; break;;
case 0x51: D=C; break;;
case 0x52: D=D; break;;
case 0x53: D=E; break;;
case 0x54: D=H; break;;
case 0x55: D=L; break;;
case 0x56: D=M; break;;
case 0x57: D=A; break;;
case 0x58: E=B; break;;
case 0x59: E=C; break;;
case 0x5a: E=D; break;;
case 0x5b: E=E; break;;
case 0x5c: E=H; break;;
case 0x5d: E=L; break;;
case 0x5e: E=M; break;;
case 0x5f: E=A; break;;
case 0x60: H=B; break;;
case 0x61: H=C; break;;
case 0x62: H=D; break;;
case 0x63: H=E; break;;
case 0x64: H=H; break;;
case 0x65: H=L; break;;
case 0x66: H=M; break;;
case 0x67: H=A; break;;
case 0x68: L=B; break;;
case 0x69: L=C; break;;
case 0x6a: L=D; break;;
case 0x6b: L=E; break;;
case 0x6c: L=H; break;;
case 0x6d: L=L; break;;
case 0x6e: L=M; break;;
case 0x6f: L=A; break;;
case 0x70: M=B; break;;
case 0x71: M=C; break;;
case 0x72: M=D; break;;
case 0x73: M=E; break;;
case 0x74: M=H; break;;
case 0x75: M=L; break;;
case 0x76: exit(0); break;;
case 0x77: M=A; break;;
case 0x78: A=B; break;;
case 0x79: A=C; break;;
case 0x7a: A=D; break;;
case 0x7b: A=E; break;;
case 0x7c: A=H; break;;
case 0x7d: A=L; break;;
case 0x7e: A=M; break;;
case 0x7f: A=A; break;;
case 0x80: arith(A, +, B); break;;
case 0x81: arith(A, +, C); break;;
case 0x82: arith(A, +, D); break;;
case 0x83: arith(A, +, E); break;;
case 0x84: arith(A, +, H); break;;
case 0x85: arith(A, +, L); break;;
case 0x86: arith(A, +, M); break;;
case 0x87: arith(A, +, A); break;;
case 0x88: arith(A, +flags.c+, B); break;;
case 0x89: arith(A, +flags.c+, C); break;;
case 0x8A: arith(A, +flags.c+, D); break;;
case 0x8B: arith(A, +flags.c+, E); break;;
case 0x8C: arith(A, +flags.c+, H); break;;
case 0x8D: arith(A, +flags.c+, L); break;;
case 0x8E: arith(A, +flags.c+, M); break;;
case 0x8F: arith(A, +flags.c+, A); break;;
case 0x90: arith(A, -, B); break;;
case 0x91: arith(A, -, C); break;;
case 0x92: arith(A, -, D); break;;
case 0x93: arith(A, -, E); break;;
case 0x94: arith(A, -, H); break;;
case 0x95: arith(A, -, L); break;;
case 0x96: arith(A, -, M); break;;
case 0x97: arith(A, -, A); break;;
case 0x98: arith(A, -flags.c-, B); break;;
case 0x99: arith(A, -flags.c-, C); break;;
case 0x9A: arith(A, -flags.c-, D); break;;
case 0x9B: arith(A, -flags.c-, E); break;;
case 0x9C: arith(A, -flags.c-, H); break;;
case 0x9D: arith(A, -flags.c-, L); break;;
case 0x9E: arith(A, -flags.c-, M); break;;
case 0x9F: arith(A, -flags.c-, A); break;;
case 0xA0: arith(A, &, B); break;;
case 0xA1: arith(A, &, C); break;;
case 0xA2: arith(A, &, D); break;;
case 0xA3: arith(A, &, E); break;;
case 0xA4: arith(A, &, H); break;;
case 0xA5: arith(A, &, L); break;;
case 0xA6: arith(A, &, M); break;;
case 0xA7: arith(A, &, A); break;;
case 0xA8: arith(A, ^, B); break;;
case 0xA9: arith(A, ^, C); break;;
case 0xAA: arith(A, ^, D); break;;
case 0xAB: arith(A, ^, E); break;;
case 0xAC: arith(A, ^, H); break;;
case 0xAD: arith(A, ^, L); break;;
case 0xAE: arith(A, ^, M); break;;
case 0xAF: arith(A, ^, A); break;;
case 0xB0: arith(A, |, B); break;;
case 0xB1: arith(A, |, C); break;;
case 0xB2: arith(A, |, D); break;;
case 0xB3: arith(A, |, E); break;;
case 0xB4: arith(A, |, H); break;;
case 0xB5: arith(A, |, L); break;;
case 0xB6: arith(A, |, M); break;;
case 0xB7: arith(A, |, A); break;;
case 0xB8: aritht(A, -, B); break;;
case 0xB9: aritht(A, -, C); break;;
case 0xBA: aritht(A, -, D); break;;
case 0xBB: aritht(A, -, E); break;;
case 0xBC: aritht(A, -, H); break;;
case 0xBD: aritht(A, -, L); break;;
case 0xBE: aritht(A, -, M); break;;
case 0xBF: aritht(A, -, A); break;;
case 0xC0: if (!flags.z) IP=pop(); break;;
case 0xC1: BC=pop(); break;;
case 0xC2: tmp=imm16(); if (!flags.z) IP=tmp; break;;
case 0xC3: IP=imm16(); break;;
case 0xC4: tmp=imm16(); if (!flags.z) call(tmp); break;;
case 0xC5: push(BC); break;;
case 0xC6: arith(A, +, imm8()); break;;
case 0xC7: interrupt(0); break;;
case 0xC8: if (flags.z) IP=pop(); break;;
case 0xC9: IP=pop(); break;;
case 0xCA: tmp=imm16(); if (flags.z) IP=tmp; break;;
case 0xCC: tmp=imm16(); if (flags.z) call(tmp); break;;
case 0xCD: call(imm16()); break;;
case 0xCE: arith(A, +flags.c+, imm8()); break;;
case 0xCF: interrupt(1); break;;
case 0xD0: if (!flags.c) IP=pop(); break;;
case 0xD1: DE=pop(); break;;
case 0xD2: tmp=imm16(); if (!flags.c) IP=tmp; break;;
case 0xD3: out(imm8()); break;;
case 0xD4: tmp=imm16(); if (!flags.c) call(tmp); break;;
case 0xD5: push(DE); break;;
case 0xD6: arith(A, -, imm8()); break;;
case 0xD7: interrupt(2); break;;
case 0xD8: if (flags.c) IP=pop(); break;;
case 0xDA: tmp=imm16(); if (flags.c) IP=tmp; break;;
case 0xDB: in(imm8()); break;;
case 0xDC: tmp=imm16(); if (flags.c) call(tmp); break;;
case 0xDE: arith(A, -flags.c-, imm8()); break;;
case 0xDF: interrupt(3); break;;
case 0xE0: if (!flags.p) IP=pop(); break;;
case 0xE1: HL=pop(); break;;
case 0xE2: tmp=imm16(); if (!flags.p) IP=tmp; break;;
case 0xE3: tmp=HL; HL=pop(); push(tmp); break;;
case 0xE4: tmp=imm16(); if (!flags.p) call(tmp); break;;
case 0xE5: push(HL); break;;
case 0xE6: arith(A, &, imm8()); break;;
case 0xE7: interrupt(4); break;;
case 0xE8: if (flags.p) IP=pop(); break;;
case 0xE9: IP=HL; break;;
case 0xEA: tmp=imm16(); if (flags.p) IP=tmp; break;;
case 0xEB: tmp=HL; HL=DE; DE=tmp;; break;;
case 0xEC: tmp=imm16(); if (flags.p) call(tmp); break;;
case 0xEE: arith(A, ^, imm8()); break;;
case 0xEF: interrupt(5); break;;
case 0xF0: if (!flags.s) IP=pop(); break;;
case 0xF1: regset.regp.psw=pop(); break;;
case 0xF2: tmp=imm16(); if (!flags.s) IP=tmp; break;;
case 0xF4: tmp=imm16(); if (!flags.s) call(tmp); break;;
case 0xF5: push(regset.regp.psw); break;;
case 0xF6: arith(A, |, imm8()); break;;
case 0xF7: interrupt(6); break;;
case 0xF8: if (flags.s) IP=pop(); break;;
case 0xF9: SP=HL; break;;
case 0xFA: tmp=imm16(); if (flags.s) IP=tmp; break;;
case 0xFC: tmp=imm16(); if (flags.s) call(tmp); break;;
case 0xFE: aritht(A, -, imm8()); break;;
case 0xFF: interrupt(7); break;;
default:
IP--;
dump();
fprintf(stderr, "Invalid opcode at IP=%04X\n", IP);
exit(1);
break;
}
}
void copy_cmdline(char* str) {
int i, c;
uint8_t *len = &mem[0x80];
char* ptr = (char*)&mem[0x81];
c = strlen(str);
// Clip at max length
if (c>0x7E) {
fprintf(stderr, "Command line too long, max is 126 bytes\n");
exit(1);
}
memcpy(ptr, str, c);
ptr[c]=0x0D;
*len=c;
}
int main(int argc, char** argv) {
memset(&mem, sizeof(mem), 0);
// Prepare default exit into int 20h
mem[0]=0x76;
push(0);
argc--;
argv++;
while(argc && argv[0][0]=='-') {
switch(argv[0][1]) {
default:
fprintf(stderr, "Unknown option %s\n", argv[0]);
exit(1);
break;
}
argc--;
argv++;
}
if (argc) {
FILE* fd = fopen(argv[0], "r");
fread(mem + IP, 1, sizeof(mem) - IP, fd);
argc--;
argv++;
} else {
fprintf(stderr, "No COM file specified\n");
exit(1);
}
if (argc) {
copy_cmdline(argv[0]);
} else {
copy_cmdline("");
}
while(1) {
dump();
step();
}
}

37
src/utils/fix-rom.c Normal file
View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
struct stat sbuf;
int main(int argc, char** argv) {
FILE* fd = fopen(argv[1], "r+");
fstat(fileno(fd), &sbuf);
if (sbuf.st_size & 0x1F) {
fprintf(stderr, "Filesize is not a multiple of 512 bytes\n");
exit(1);
}
// Fill out filesize flag
fseek(fd, 2, SEEK_SET);
fputc(sbuf.st_size >> 9, fd);
// Calculate checksum
fseek(fd, 0, SEEK_SET);
off_t i;
uint8_t s;
for (i=0; i<sbuf.st_size; i++) {
s+=fgetc(fd);
}
// Edit last byte so that checksum is 0
fseek(fd, -1, SEEK_END);
s=fgetc(fd)-s;
fseek(fd, -1, SEEK_END);
fputc(s, fd);
fclose(fd);
}