Added Jaguar Object Processor assembler.

Rationale for this and how it works can be found in
docs/note-on-the-op-assembler.txt. Version now at 1.13.0.
This commit is contained in:
Shamus Hammons 2018-02-25 21:39:59 -06:00
parent ae528e6fdb
commit 261f8d9198
21 changed files with 924 additions and 83 deletions

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
68kgen.o
68ktab.h
68kmn
6502kw.h
opkw.h
*.o
*~
kwtab.h

View File

@ -79,6 +79,7 @@ int d_cstruct(void);
int d_prgflags(void);
int d_opt(void);
int d_dsp(void);
int d_objproc(void);
void SetLargestAlignment(int);
// Directive handler table
@ -149,7 +150,8 @@ int (*dirtab[])() = {
d_68882, // 63 .68882
d_56001, // 64 .56001
d_nofpu, // 65 nofpu
d_opt, // 58 .opt
d_opt, // 66 .opt
d_objproc, // 67 .objproc
};
@ -216,8 +218,8 @@ int d_org(void)
{
uint64_t address;
if (!rgpu && !rdsp && !m6502)
return error(".org permitted only in gpu/dsp and 6502 sections");
if (!rgpu && !rdsp && !robjproc && !m6502)
return error(".org permitted only in GPU/DSP/OP and 6502 sections");
if (abs_expr(&address) == ERROR)
{
@ -225,7 +227,7 @@ int d_org(void)
return ERROR;
}
if (rgpu | rdsp)
if (rgpu | rdsp | robjproc)
{
orgaddr = address;
orgactive = 1;
@ -1066,9 +1068,6 @@ int d_dc(WORD siz)
uint16_t tdb = eattr & TDB;
uint16_t defined = eattr & DEFINED;
if ((challoc - ch_size) < 4)
chcheck(4);
switch (siz)
{
case SIZB:
@ -1136,6 +1135,7 @@ int d_dc(WORD siz)
D_long(eval);
}
break;
case SIZQ:
// 64-bit size
@ -1144,7 +1144,17 @@ int d_dc(WORD siz)
// Shamus: We only handle DC.Q type stuff, will have to add fixups
// and stuff later (maybe... might not be needed...)
D_quad(eval);
// DEFINITELY NEED FIXUPS HERE!
if (!defined)
{
AddFixup(FU_QUAD, sloc, exprbuf);
D_quad(0LL);
}
else
{
D_quad(eval);
}
break;
case SIZS:
// 32-bit float size
@ -1158,8 +1168,9 @@ int d_dc(WORD siz)
}
else
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MSINGLE, NULL);
//Would this *ever* happen?
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MSINGLE, NULL);
PTR ptr;
ptr.u64 = &eval;
@ -1180,8 +1191,9 @@ int d_dc(WORD siz)
}
else
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MDOUBLE, NULL);
//Would this *ever* happen?
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MDOUBLE, NULL);
PTR ptr;
ptr.u64 = &eval;
@ -1204,8 +1216,9 @@ int d_dc(WORD siz)
}
else
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MEXTEND, NULL);
//Would this *ever* happen?
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MEXTEND, NULL);
PTR ptr;
ptr.u64 = &eval;
@ -1483,7 +1496,7 @@ int d_nlist(void)
//
int d_68000(void)
{
rgpu = rdsp = 0;
rgpu = rdsp = robjproc = 0;
// Switching from gpu/dsp sections should reset any ORG'd Address
orgactive = 0;
orgwarning = 0;
@ -1601,6 +1614,7 @@ int d_gpu(void)
rgpu = 1; // Set GPU assembly
rdsp = 0; // Unset DSP assembly
robjproc = 0; // Unset OP assembly
regbank = BANK_N; // Set no default register bank
return 0;
}
@ -1626,6 +1640,7 @@ int d_dsp(void)
rdsp = 1; // Set DSP assembly
rgpu = 0; // Unset GPU assembly
robjproc = 0; // Unset OP assembly
regbank = BANK_N; // Set no default register bank
return 0;
}
@ -1876,6 +1891,32 @@ int d_cstruct(void)
}
//
// Define start of OP object list (allows the use of ORG)
//
int d_objproc(void)
{
if ((cursect != TEXT) && (cursect != DATA))
{
error(".objproc can only be used in the TEXT or DATA segments");
return ERROR;
}
// If previous section was DSP or 68000 then we need to reset ORG'd
// Addresses
if (!robjproc)
{
orgactive = 0;
orgwarning = 0;
}
robjproc = 1; // Set OP assembly
rgpu = 0; // Unset GPU assembly
rdsp = 0; // Unset DSP assembly
return OK;
}
//
// Undefine a macro - .undefmac macname [, macname...]
//

View File

@ -0,0 +1,129 @@
A Few Notes on the New Object Processor Assembler
-------------------------------------------------
Q: What is it?
A: An assembler to generate object lists for the Atari Jaguar's Object
processor.
Q: Why is it here?
A: To really utilize the OP properly, it needs an assembler. Otherwise, what
happens is you end up writing an assembler in your code to assemble the OP
list, and that's a real drag--something that *should* be handled by a proper
assembler.
Q: How do I use it?
A: The OP assembler works similarly to the RISC assembler; to enter the OP
assembler, you put the .objproc directive in your code (N.B.: like the RISC
assembler, it only works in a TEXT or DATA section). From there, you build
the OP list how you want it and go from there. A few caveats: you will want
to put a .org directive at the top of your list, and labels that you want to
be able to address in 68xxx code (for moving from a data section to an
address where it will be executed by the OP, for example) should be created
in .68xxx mode.
Q: What are the opcodes?
A: They are bitmap, scbitmap, gpuobj, branch, stop, nop, and jump. nop and jump
are psuedo-ops, they are there as a convenience to the coder.
Q: What are the proper forms for these opcodes?
A: They are as follows:
bitmap <data addr>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>, <bpp>,
<pallete idx>, <flags>, <firstpix>, <pitch>
scbitmap <data addr>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>,
<xscale>, <yscale>, <remainder>, <bpp>, <pallete idx>,
<flags>, <firstpix>, <pitch>
gpuobj <line #>, <userdata> (bits 14-63 of this object)
branch VC <condition (<, =, >)> <line #>, <link addr>
branch OPFLAG, <link addr>
branch SECHALF, <link addr>
stop
nop
jump <link addr>
Note that the <flags> field in bitmap and scbitmap objects consist of the
following: REFLECT, RMW, TRANS, RELEASE. They can be in any order (and
should be separated by whitespace *only*), and you can only put a maximum of
four of them in. Further note that with bitmap and scbitmap objects, all the
parameters after <data addr> are optional--if they are omitted, they will
use defaults (mostly 0, but 1 is the default for pitch). Also, in the
scbitmap object, the <xscale>, <yscale>, and <remainder> fields can be
floating point constants/expressions. <data addr> can refer to any address
defined (even external!) and the linker (rln v1.6.0 or greater) will
properly fix up the address.
Q: What do they do?
A: Pretty much what you expect. It's beyond the scope of this little note to
explain the Jaguar's Object Processor and how it operates, so you'll have to
seek explanations for how they work elsewhere.
Q: Why do I want to put a .org directive at the top of my list?
A: You want to put a .org directive at the top of your list because otherwise
the assembler will not know where in memory the object list is supposed
go--then when you move it to its destination, the object link addresses will
all be wrong and it won't work.
Q: Why would I copy my object list to another memory location?
A: Simple: because the OP destroys the list as it uses it to render the screen.
If you don't keep a fresh copy stashed away somewhere to refresh it before
the next frame is rendered, what you see on the screen will not be what you
expect, as the OP has scribbled all over it!
Q: Does the assembler do anything behind my back?
A: Yes, it will emit NOPs to ensure that bitmaps and scbitmaps are on proper
memory boundaries, and fixup link addresses as necessary. This is needed
because of a quirk in how the OP works (it ORs constants on the address
lines to get the phrases it needs and if they are not zeroes, it will fail
in bizarre ways).
Q: Why can't I define the link addresses for all the objects?
A: You really, *really* don't want to do this. Trust me on this one.
Q: How about an example of an object list?
A: Here you go:
objList = $10000
bRam = $20000
;
.68000
objects: ; This is the label you will use to address this in 68K code
.objproc ; Engage the OP assembler
.org objList ; Tell the OP assembler where the list will execute
;
branch VC < 69, .stahp ; Branch to the STOP object if VC < 69
branch VC > 241, .stahp ; Branch to the STOP object if VC > 241
bitmap bRAM, 22, 70, 24, 24, 22, 4
bitmap bRAM, 20+96+96, 70, 24, 24, 22, 4, 0, REFLECT
scbitmap tms, 20, 70, 1, 1, 8, 3.0, 3.0, 2.9999, 0, 0, TRANS
scbitmap tmsShadow, 23, 73, 1, 1, 8, 3.0, 3.0, 2.9999, 0, 3, TRANS
bitmap sbRelBM, 30, 108, 3, 3, 8, 0, 1, TRANS
bitmap txt1BM, 46, 132, 3, 3, 8, 0, 2, TRANS
bitmap txt2BM, 46, 148, 3, 3, 8, 0, 2, TRANS
bitmap txt3BM, 22, 164, 3, 3, 8, 0, 2, TRANS
jump .haha
.stahp:
stop
.haha:
jump .stahp

View File

@ -214,8 +214,9 @@ int eaNgen(WORD siz)
// 68881/68882/68040 only
if (w)
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MSINGLE, NULL);
//Would a floating point value *ever* need to be fixed up as if it were an address? :-P
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MSINGLE, NULL);
// The value passed back from expr() is an internal C double;
// so we have to access it as such then convert it to an
@ -238,8 +239,9 @@ int eaNgen(WORD siz)
// 68881/68882/68040 only
if (w)
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MDOUBLE, NULL);
//Would a floating point value *ever* need to be fixed up as if it were an address? :-P
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MDOUBLE, NULL);
PTR p;
p.u64 = &aNexval;
@ -258,8 +260,9 @@ int eaNgen(WORD siz)
// 68881/68882/68040 only
if (w)
{
if (tdb)
MarkRelocatable(cursect, sloc, tdb, MEXTEND, NULL);
//Would a floating point value *ever* need to be fixed up as if it were an address? :-P
// if (tdb)
// MarkRelocatable(cursect, sloc, tdb, MEXTEND, NULL);
PTR p;
p.u64 = &aNexval;

View File

@ -14,6 +14,7 @@
#include "fltpoint.h"
#include <float.h>
#include <math.h>
#include <stdio.h>
//
// Check for IEEE-754 conformance (C99 compilers should be OK here)
@ -169,3 +170,25 @@ void DoubleToExtended(double d, uint8_t out[])
out[11] = intMant & 0xFF;
}
//
// Convert a host native floating point number to a fixed point number.
//
uint64_t DoubleToFixedPoint(double d, int intBits, int fracBits)
{
uint8_t signBit = (signbit(d) ? 1 : 0);
// Ensure what we're working on is positive...
if (d < 0)
d *= -1;
double scaleFactor = (double)(1 << fracBits);
uint64_t result = (uint64_t)(d * scaleFactor);
// Invert the result, if necessary
if (signBit == 1)
result = (result = 0xFFFFFFFFFFFFFFFFLL) + 1;
return result;
}

View File

@ -1,13 +1,20 @@
//
// Cross-platform floating point handling
// Cross-platform floating point handling (fixed point handling too!)
//
// by James Hammons
// (C) 2018 Underground Software
//
#ifndef __FLTPOINT_H__
#define __FLTPOINT_H__
#include <stdint.h>
uint32_t FloatToIEEE754(float f);
uint64_t DoubleToIEEE754(double d);
void DoubleToExtended(double d, uint8_t out[]);
uint64_t DoubleToFixedPoint(double d, int intBits, int fracBits);
#endif // __FLTPOINT_H__

View File

@ -27,15 +27,15 @@ HOSTCC = gcc
#CFLAGS = -std=$(STD) -D_DEFAULT_SOURCE -g -D__GCCUNIX__ -I. -O2 -MMD
CFLAGS = -std=$(STD) -D_DEFAULT_SOURCE -g -D__GCCUNIX__ -I. -O2
SRCS = 6502.c amode.c debug.c direct.c eagen.c error.c expr.c fltpoint.c listing.c mach.c macro.c mark.c object.c procln.c riscasm.c rmac.c sect.c symbol.c token.c
SRCS = 6502.c amode.c debug.c direct.c eagen.c error.c expr.c fltpoint.c listing.c mach.c macro.c mark.c object.c op.c procln.c riscasm.c rmac.c sect.c symbol.c token.c
OBJS = 6502.o amode.o debug.o direct.o eagen.o error.o expr.o fltpoint.o listing.o mach.o macro.o mark.o object.o procln.o riscasm.o rmac.o sect.o symbol.o token.o
OBJS = 6502.o amode.o debug.o direct.o eagen.o error.o expr.o fltpoint.o listing.o mach.o macro.o mark.o object.o op.o procln.o riscasm.o rmac.o sect.o symbol.o token.o
#
# Build everything
#
all : mntab.h 68ktab.h kwtab.h risckw.h 6502kw.h rmac
all : mntab.h 68ktab.h kwtab.h risckw.h 6502kw.h opkw.h rmac
@echo
@echo "Don't forget to bump the version number before commiting!"
@echo
@ -61,6 +61,9 @@ kwtab.h : kwtab kwgen
risckw.h : kwtab kwgen
./kwgen mr <risctab >risckw.h
opkw.h : op.tab kwgen
./kwgen mo <op.tab >opkw.h
#
# Build tools
#
@ -120,6 +123,9 @@ mark.o : mark.c mark.h
object.o : object.c object.h
$(CC) $(CFLAGS) -c object.c
op.o : op.c op.h
$(CC) $(CFLAGS) -c op.c
procln.o : procln.c procln.h
$(CC) $(CFLAGS) -c procln.c
@ -151,41 +157,44 @@ clean:
#
# Dependencies
#
6502.o: 6502.c direct.h rmac.h symbol.h expr.h error.h mach.h procln.h \
token.h riscasm.h sect.h
6502.o: 6502.c direct.h rmac.h symbol.h token.h expr.h error.h mach.h \
procln.h riscasm.h sect.h
68kgen.o: 68kgen.c
amode.o: amode.c amode.h rmac.h symbol.h error.h expr.h mach.h procln.h \
token.h sect.h kwtab.h mntab.h parmode.h
debug.o: debug.c debug.h rmac.h symbol.h amode.h direct.h mark.h sect.h \
token.h
direct.o: direct.c direct.h rmac.h symbol.h 6502.h amode.h error.h expr.h \
listing.h mach.h macro.h mark.h procln.h token.h riscasm.h sect.h \
kwtab.h fltpoint.h
eagen.o: eagen.c eagen.h rmac.h symbol.h amode.h sect.h mark.h error.h \
mach.h riscasm.h eagen0.c fltpoint.h
error.o: error.c error.h rmac.h symbol.h token.h listing.h
expr.o: expr.c expr.h rmac.h symbol.h direct.h error.h listing.h mach.h \
procln.h token.h riscasm.h sect.h kwtab.h
debug.o: debug.c debug.h rmac.h symbol.h amode.h direct.h token.h expr.h \
mark.h sect.h
direct.o: direct.c direct.h rmac.h symbol.h token.h 6502.h amode.h \
error.h expr.h fltpoint.h listing.h mach.h macro.h mark.h procln.h \
riscasm.h sect.h kwtab.h
eagen.o: eagen.c eagen.h rmac.h symbol.h amode.h error.h fltpoint.h \
mach.h mark.h riscasm.h sect.h token.h eagen0.c
error.o: error.c error.h rmac.h symbol.h listing.h token.h
expr.o: expr.c expr.h rmac.h symbol.h direct.h token.h error.h listing.h \
mach.h procln.h riscasm.h sect.h kwtab.h
fltpoint.o: fltpoint.c fltpoint.h
kwgen.o: kwgen.c
listing.o: listing.c listing.h rmac.h symbol.h error.h procln.h token.h \
sect.h version.h
mach.o: mach.c mach.h rmac.h symbol.h amode.h direct.h eagen.h error.h \
procln.h token.h riscasm.h sect.h kwtab.h 68ktab.h
macro.o: macro.c macro.h rmac.h symbol.h debug.h direct.h error.h expr.h \
listing.h procln.h token.h
mach.o: mach.c mach.h rmac.h symbol.h amode.h direct.h token.h eagen.h \
error.h expr.h procln.h riscasm.h sect.h kwtab.h 68ktab.h
macro.o: macro.c macro.h rmac.h symbol.h debug.h direct.h token.h error.h \
expr.h listing.h procln.h
mark.o: mark.c mark.h rmac.h symbol.h error.h object.h riscasm.h sect.h
object.o: object.c object.h rmac.h symbol.h 6502.h error.h mark.h \
riscasm.h sect.h
object.o: object.c object.h rmac.h symbol.h 6502.h direct.h token.h \
error.h mark.h riscasm.h sect.h
op.o: op.c op.h rmac.h symbol.h direct.h token.h error.h expr.h \
fltpoint.h mark.h procln.h riscasm.h sect.h
procln.o: procln.c procln.h rmac.h symbol.h token.h 6502.h amode.h \
direct.h error.h expr.h listing.h mach.h macro.h riscasm.h sect.h \
kwtab.h mntab.h risckw.h 6502kw.h
riscasm.o: riscasm.c riscasm.h rmac.h symbol.h amode.h direct.h error.h \
expr.h mark.h procln.h token.h sect.h risckw.h kwtab.h
rmac.o: rmac.c rmac.h symbol.h 6502.h debug.h direct.h error.h expr.h \
listing.h mark.h macro.h object.h procln.h token.h riscasm.h sect.h \
direct.h error.h expr.h listing.h mach.h macro.h op.h riscasm.h sect.h \
kwtab.h mntab.h risckw.h 6502kw.h opkw.h
riscasm.o: riscasm.c riscasm.h rmac.h symbol.h amode.h direct.h token.h \
error.h expr.h mark.h procln.h sect.h risckw.h kwtab.h
rmac.o: rmac.c rmac.h symbol.h 6502.h debug.h direct.h token.h error.h \
expr.h listing.h mark.h macro.h object.h procln.h riscasm.h sect.h \
version.h
sect.o: sect.c sect.h rmac.h symbol.h 6502.h direct.h error.h expr.h \
listing.h mach.h mark.h riscasm.h token.h
sect.o: sect.c sect.h rmac.h symbol.h 6502.h direct.h token.h error.h \
expr.h listing.h mach.h mark.h riscasm.h
symbol.o: symbol.c symbol.h error.h rmac.h listing.h object.h procln.h \
token.h
token.o: token.c token.h rmac.h symbol.h direct.h error.h macro.h \

38
mark.c
View File

@ -71,9 +71,9 @@ void StopMark(void)
uint32_t MarkRelocatable(uint16_t section, uint32_t loc, uint16_t to, uint16_t flags, SYM * symbol)
{
#ifdef DEBUG_IMAGE_MARKING
printf("MarkRelocatable: section=%i, loc=$%X, to=$%X, flags=$%x, symbol=$%X\n", section, loc, to, flags, symbol);
printf("MarkRelocatable: section=%i, loc=$%X, to=$%X, flags=$%x, symbol=%p\n", section, loc, to, flags, symbol);
if (symbol)
printf(" symbol->stype=$%02X, sattr=$%04X, sattre=$%08X, svalue=%i, sname=%s\n", symbol->stype, symbol->sattr, symbol->sattre, symbol->svalue, symbol->sname);
printf(" symbol->stype=$%02X, sattr=$%04X, sattre=$%08X, svalue=%li, sname=%s\n", symbol->stype, symbol->sattr, symbol->sattre, symbol->svalue, symbol->sname);
#endif
if ((mcalloc - mcused) < MIN_MARK_MEM)
@ -380,9 +380,13 @@ printf(" validsegment: raddr = $%08X\n", loc);
// This tells the linker to do a WORD relocation (otherwise it
// defaults to doing a LONG, throwing things off for WORD sized
// fixups)
if (!(w & MLONG))
if (!(w & (MLONG | MQUAD)))
rflag |= 0x00000002;
// Tell the linker that the fixup is an OL QUAD data address
if (w & MQUAD)
rflag |= 0x00000004;
if (symbol != NULL)
{
// Deposit external reference
@ -415,19 +419,31 @@ printf(" validsegment(3): rflag = $%08X\n", rflag);
if (w & (DATA | BSS))
{
uint8_t * dp = objImage + BSDHDRSIZE + loc;
uint32_t olBitsSave = 0;
// Bump the start of the section if it's DATA (& not TEXT)
if (from == DATA)
dp += tsize;
uint32_t diff = (rflag & 0x02 ? GETBE16(dp, 0) : GETBE32(dp, 0));
// Special handling for OP (data addr) relocation...
if (rflag & 0x04)
{
olBitsSave = diff & 0x7FF;
diff = (diff & 0xFFFFF800) >> 8;
}
DEBUG printf("diff=%uX ==> ", diff);
#ifdef DEBUG_IMAGE_MARKING
printf(" validsegment(4): diff = $%08X --> ", diff);
printf(" validsegment(4): diff = $%08X ", diff);
#endif
if (rflag & 0x01)
diff = WORDSWAP32(diff);
#ifdef DEBUG_IMAGE_MARKING
printf("(sect[TEXT].sloc=$%X) --> ", sect[TEXT].sloc);
#endif
diff += sect[TEXT].sloc;
if (w == BSS)
@ -442,11 +458,21 @@ printf(" validsegment(4): diff = $%08X --> ", diff);
// thus leaving a naked semicolon afterwards to
// screw up the if/else structure. This is the price
// you pay when using macros pretending to be code.
if (rflag & 0x02)
if (rflag & 0x02) // WORD relocation
{
SETBE16(dp, 0, diff);
}
else
else if (rflag & 0x04) // OP data address relocation
{
// We do it this way because we might have an offset
// that is not a multiple of 8 and thus we need this in
// place to prevent a bad address at link time. :-P As
// a consequence of this, the highest address we can
// have here is $1FFFF8.
diff = ((diff & 0x001FFFFF) << 11) | olBitsSave;
SETBE32(dp, 0, diff);
}
else // LONG relocation
{
SETBE32(dp, 0, diff);
}

7
mark.h
View File

@ -26,14 +26,15 @@ MCHUNK {
#define MWORD 0x0000 // Marked word
#define MLONG 0x0100 // Marked long
#define MQUAD 0x0400 // Marked quad
//This will have to be defined eventually. Might have to overhaul the mark
//system as 8-bits doesn't seem to be enough, at least for a bitfield (which it
//might not have to be, in which case it would be big enough...)
//#define MQUAD 0x // Marked quad word (TODO: merge with MDOUBLE?)
#define MMOVEI 0x0200 // Mark RISC MOVEI instruction
#define MDOUBLE 0x0400 // Marked double float
#define MEXTEND 0x0800 // Marked extended float
#define MSINGLE 0x0880 // Marked single float (TODO: merge with MLONG?)
//#define MDOUBLE 0x0400 // Marked double float
//#define MEXTEND 0x0800 // Marked extended float
//#define MSINGLE 0x0880 // Marked single float (TODO: merge with MLONG?)
#define MGLOBAL 0x0800 // Mark contains global
#define MPCREL 0x1000 // Mark is PC-relative
#define MCHEND 0x2000 // Indicates end of mark chunk

8
mntab
View File

@ -110,9 +110,10 @@ prgflags 57
nofpu 65
.opt 66
opt 66
.objproc 67
.if 500
if 500
.if 500
if 500
.else 501
else 501
.endc 502
@ -120,7 +121,7 @@ endc 502
.endif 502
endif 502
.iif 503
iif 503
iif 503
.macro 504
macro 504
.endm 505
@ -131,3 +132,4 @@ rept 506
endr 507
.exitm 510
exitm 510

487
op.c Normal file
View File

@ -0,0 +1,487 @@
//
// Jaguar Object Processor assembler
//
// by James Hammons
// (C) 2018 Underground Software
//
#include "op.h"
#include "direct.h"
#include "error.h"
#include "expr.h"
#include "fltpoint.h"
#include "mark.h"
#include "procln.h"
#include "riscasm.h"
#include "rmac.h"
#include "sect.h"
#include "token.h"
// Macros to help define things (though largely unnecessary for this assembler)
#define BITMAP 3100
#define SCBITMAP 3101
#define GPUOBJ 3102
#define BRANCH 3103
#define STOP 3104
#define NOP 3105
#define JUMP 3106
// Function prototypes
int HandleBitmap(void);
int HandleScaledBitmap(void);
int HandleGPUObject(void);
int HandleBranch(void);
int HandleStop(void);
int HandleNOP(void);
int HandleJump(void);
// OP assembler vars.
static uint8_t lastObjType;
static uint32_t lastSloc;
static char scratchbuf[4096];
static TOKEN fixupExpr[4] = { CONST, 0, 0, ENDEXPR };
//static PTR fixupPtr = { .tk = (fixupExpr + 1) }; // C99 \o/
static PTR fixupPtr = { (uint8_t *)(fixupExpr + 1) }; // meh, it works
//
// The main Object Processor assembler. Basically just calls the sub functions
// to generate the appropriate code.
//
int GenerateOPCode(int state)
{
if (!robjproc)
return error("opcode only valid in OP mode");
switch (state)
{
case BITMAP:
return HandleBitmap();
case SCBITMAP:
return HandleScaledBitmap();
case GPUOBJ:
return HandleGPUObject();
case BRANCH:
return HandleBranch();
case STOP:
return HandleStop();
case NOP:
return HandleNOP();
case JUMP:
return HandleJump();
}
return error("unknown OP opcode");
}
static inline void GetSymbolUCFromTokenStream(char * s)
{
strcpy(s, string[tok[1]]);
strtoupper(s);
tok += 2;
}
static inline uint64_t CheckFlags(char * s)
{
GetSymbolUCFromTokenStream(s);
if (strcmp(scratchbuf, "REFLECT") == 0)
return 0x01;
else if (strcmp(scratchbuf, "RMW") == 0)
return 0x02;
else if (strcmp(scratchbuf, "TRANS") == 0)
return 0x04;
else if (strcmp(scratchbuf, "RELEASE") == 0)
return 0x08;
return 0;
}
//
// Define a bitmap object
// Form: bitmap <data>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>, <bpp>,
// <pallete idx>, <flags>, <firstpix>, <pitch>
//
int HandleBitmap(void)
{
uint64_t xpos = 0;
uint64_t ypos = 0;
uint64_t iheight = 0;
uint64_t dwidth = 0;
uint64_t iwidth = 0;
uint64_t bpp = 0;
uint64_t index = 0;
uint64_t flags = 0;
uint64_t firstpix = 0;
uint64_t pitch = 1;
uint64_t eval;
uint16_t eattr;
SYM * esym = 0;
if ((orgaddr & 0x0F) != 0)
{
warn("bitmap not on double phrase boundary");
// Fixup org address (by emitting a NOP)...
HandleNOP();
// We don't need to do a fixup here, because we're guaranteed that the
// last object, if it was a scaled bitmap *or* regular bitmap, will
// already be on the correct boundary, so we won't have to fix it up.
}
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
AddFixup(FU_QUAD | FU_OBJDATA, sloc, exprbuf);
else if (eattr & TDB)
MarkRelocatable(cursect, sloc, (eattr & TDB), MQUAD, NULL);
uint64_t dataAddr = eval & 0xFFFFF8;
uint64_t linkAddr = (orgaddr + 16) & 0x3FFFF8;
uint64_t * vars[10] = { &xpos, &ypos, &dwidth, &iwidth, &iheight, &bpp, &index, 0, &firstpix, &pitch };
for(int i=0; i<10; i++)
{
// If there are less than 10 arguments after the data address, use
// defaults for the rest of them
// N.B.: Should there be a default minimum # of args? Like the first 5?
if (tok[0] == EOL)
break;
CHECK_COMMA;
if (i != 7)
{
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
return error("bad expression");
*vars[i] = eval;
}
else
{
// Handle FLAGs. Can have at most four.
for(int j=0; j<4; j++)
{
if (tok[0] != SYMBOL)
return error("missing REFLECT, RMW, TRANS, and/or RELEASE");
flags |= CheckFlags(scratchbuf);
// Break out if no more symbols...
if (tok[0] != SYMBOL)
break;
}
}
}
at_eol();
uint64_t p1 = 0x00 | ((ypos * 2) << 3) | (iheight << 14) | (linkAddr << 21) | (dataAddr << 40);
uint64_t p2 = xpos | (bpp << 12) | (pitch << 15) | (dwidth << 18) | (iwidth << 28) | (index << 38) | (flags << 45) | (firstpix << 49);
lastSloc = sloc;
lastObjType = 0;
D_quad(p1);
D_quad(p2);
return OK;
}
//
// Define a scaled bitmap object
// Form: scbitmap <data>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>,
// <xscale>, <yscale>, <remainder>, <bpp>, <pallete idx>,
// <flags>, <firstpix>, <pitch>
//
int HandleScaledBitmap(void)
{
uint64_t xpos = 0;
uint64_t ypos = 0;
uint64_t iheight = 0;
uint64_t dwidth = 0;
uint64_t iwidth = 0;
uint64_t bpp = 0;
uint64_t index = 0;
uint64_t flags = 0;
uint64_t firstpix = 0;
uint64_t pitch = 1;
uint64_t xscale = 0;
uint64_t yscale = 0;
uint64_t remainder = 0;
uint64_t eval;
uint16_t eattr;
SYM * esym = 0;
if ((orgaddr & 0x1F) != 0)
{
warn("scaled bitmap not on quad phrase boundary");
// We only have to do a fixup here if the previous object was a bitmap,
// as it can live on a 16-byte boundary while scaled bitmaps can't. If
// the previous object was a scaled bitmap, it is guaranteed to have
// been aligned, therefore no fixup is necessary.
if (lastObjType == 0)
{
*fixupPtr.u64 = (orgaddr + 0x18) & 0xFFFFFFE0;
AddFixup(FU_QUAD | FU_OBJLINK, lastSloc, fixupExpr);
}
switch (orgaddr & 0x1F)
{
case 0x08: HandleNOP(); // Emit 3 NOPs
case 0x10: HandleNOP(); // Emit 2 NOPs
case 0x18: HandleNOP(); // Emit 1 NOP
}
}
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
AddFixup(FU_QUAD | FU_OBJDATA, sloc, exprbuf);
else if (eattr & TDB)
MarkRelocatable(cursect, sloc, (eattr & TDB), MQUAD, NULL);
uint64_t dataAddr = eval & 0xFFFFF8;
uint64_t linkAddr = (orgaddr + 32) & 0x3FFFF8;
uint64_t * vars[13] = { &xpos, &ypos, &dwidth, &iwidth, &iheight, &xscale, &yscale, &remainder, &bpp, &index, 0, &firstpix, &pitch };
for(int i=0; i<13; i++)
{
// If there are less than 13 arguments after the data address, use
// defaults for the rest of them
// N.B.: Should there be a default minimum # of args? Like the first 5?
if (tok[0] == EOL)
break;
CHECK_COMMA;
if (i != 10)
{
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
return error("bad expression");
// Handle 3.5 fixed point nums (if any)...
if ((i >= 5) && (i <= 7))
{
if (eattr & FLOAT) // Handle floats
{
// PTR p = { .u64 = &eval }; // C99 \o/
PTR p = { (uint8_t *)&eval }; // Meh, it works
eval = DoubleToFixedPoint(*p.dp, 3, 5);
}
else
eval <<= 5; // Otherwise, it's just an int...
}
*vars[i] = eval;
}
else
{
// Handle FLAGs. Can have at most four.
for(int j=0; j<4; j++)
{
if (tok[0] != SYMBOL)
return error("missing REFLECT, RMW, TRANS, and/or RELEASE");
flags |= CheckFlags(scratchbuf);
// Break out if no more symbols...
if (tok[0] != SYMBOL)
break;
}
}
}
at_eol();
uint64_t p1 = 0x01 | ((ypos * 2) << 3) | (iheight << 14) | (linkAddr << 21) | (dataAddr << 40);
uint64_t p2 = xpos | (bpp << 12) | (pitch << 15) | (dwidth << 18) | (iwidth << 28) | (index << 38) | (flags << 45) | (firstpix << 49);
uint64_t p3 = (xscale & 0xFF) | (yscale & 0xFF) << 8 | (remainder & 0xFF) << 16;
lastSloc = sloc;
lastObjType = 1;
D_quad(p1);
D_quad(p2);
D_quad(p3);
D_quad(0LL);
return OK;
}
//
// Insert GPU object
// Form: gpuobj <line #>, <userdata> (bits 14-63 of this object)
//
int HandleGPUObject(void)
{
uint64_t eval;
uint16_t eattr;
SYM * esym = 0;
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
return error("bad expression in y position");
uint64_t ypos = eval;
CHECK_COMMA;
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
return error("bad expression in data");
at_eol();
uint64_t p1 = 0x02 | ((ypos * 2) << 3) | (eval << 14);
lastObjType = 2;
D_quad(p1);
return OK;
}
//
// Insert a branch object
// Form: branch VC <condition (<, =, >)> <line #>, <link addr>
// branch OPFLAG, <link addr>
// branch SECHALF, <link addr>
//
int HandleBranch(void)
{
char missingKeyword[] = "missing VC, OPFLAG, or SECHALF in branch";
uint32_t cc = 0;
uint32_t ypos = 0;
uint64_t eval;
uint16_t eattr;
SYM * esym = 0;
if (tok[0] != SYMBOL)
return error(missingKeyword);
GetSymbolUCFromTokenStream(scratchbuf);
if (strcmp(scratchbuf, "VC") == 0)
{
switch (*tok++)
{
case '=': cc = 0; break;
case '<': cc = 1; break;
case '>': cc = 2; break;
default:
return error("missing '<', '>', or '='");
}
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
return error("bad expression");
ypos = (uint32_t)eval;
}
else if (strcmp(scratchbuf, "OPFLAG") == 0)
cc = 3;
else if (strcmp(scratchbuf, "SECHALF") == 0)
cc = 4;
else
return error(missingKeyword);
CHECK_COMMA;
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
at_eol();
uint64_t p1 = 0x03 | (cc << 14) | ((ypos * 2) << 3) | ((eval & 0x3FFFF8) << 21);
lastObjType = 3;
D_quad(p1);
return OK;
}
//
// Insert a stop object
// Form: stop
//
int HandleStop(void)
{
lastObjType = 4;
D_quad(4LL);
return OK;
}
//
// Insert a phrase sized "NOP" in the object list (psuedo-op)
// Form: nop
//
int HandleNOP(void)
{
uint64_t eval = (orgaddr + 8) & 0x3FFFF8;
// This is "branch if VC > 2047". Branch addr is next phrase, so either way
// it will pass by this phrase.
uint64_t p1 = 0x03 | (2 << 14) | (0x7FF << 3) | (eval << 21);
lastObjType = 3;
D_quad(p1);
return OK;
}
//
// Insert an unconditional jump in the object list (psuedo-op)
// Form: jump <link addr>
//
int HandleJump(void)
{
uint64_t eval;
uint16_t eattr;
SYM * esym = 0;
if (expr(exprbuf, &eval, &eattr, &esym) != OK)
return ERROR;
if (!(eattr & DEFINED))
AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
at_eol();
// This is "branch if VC < 2047", which pretty much guarantees the branch.
uint64_t p1 = 0x03 | (1 << 14) | (0x7FF << 3) | ((eval & 0x3FFFF8) << 21);
lastObjType = 3;
D_quad(p1);
return OK;
}

15
op.h Normal file
View File

@ -0,0 +1,15 @@
//
// Object Processor assembler
//
// by James Hammons
// (C) 2018 Underground Software
//
#ifndef __OP_H__
#define __OP_H__
// Exported functions
int GenerateOPCode(int);
#endif // __OP_H__

11
op.tab Normal file
View File

@ -0,0 +1,11 @@
#
# Jaguar Object Processor mnemonics
#
bitmap 3100
scbitmap 3101
gpuobj 3102
branch 3103
stop 3104
nop 3105
jump 3106

View File

@ -15,6 +15,7 @@
#include "listing.h"
#include "mach.h"
#include "macro.h"
#include "op.h"
#include "riscasm.h"
#include "sect.h"
#include "symbol.h"
@ -34,6 +35,10 @@
#define DECL_MP // Include 6502 keyword state machine tables
#include "6502kw.h"
#define DEF_MO // Include OP keyword definitions
#define DECL_MO // Include OP keyword state machine tables
#include "opkw.h"
IFENT * ifent; // Current ifent
static IFENT ifent0; // Root ifent
IFENT * f_ifent; // Freelist of ifents
@ -659,6 +664,38 @@ When checking to see if it's already been equated, issue a warning.
}
}
// If we are in OP mode and still in need of a mnemonic then search for one
if (robjproc && ((state < 0) || (state >= 1000)))
{
for(state=0, p=opname; state>=0;)
{
j = mobase[state] + (int)tolowertab[*p];
// Reject, character doesn't match
if (mocheck[j] != state)
{
state = -1; // No match
break;
}
// Must accept or reject at EOS
if (!*++p)
{
state = moaccept[j]; // (-1 on no terminal match)
break;
}
state = motab[j];
}
// Call OP code generator if we found a mnemonic
if (state >= 3100)
{
GenerateOPCode(state);
goto loop;
}
}
// Invoke macro or complain about bad mnemonic
if (state < 0)
{

View File

@ -13,6 +13,7 @@
#include "expr.h"
#include "mark.h"
#include "procln.h"
#include "rmac.h"
#include "sect.h"
#include "token.h"
@ -134,16 +135,6 @@ static const char * malformErr[] = {
};
//
// Convert a string to uppercase
//
static void strtoupper(char * s)
{
while (*s)
*s++ &= 0xDF;
}
//
// Function to return "malformed expression" error
// This is done mainly to remove a bunch of GOTO statements in the parser

View File

@ -32,7 +32,7 @@
#define GPUONLY 0x4000 // Opcode is for the GPU Only
#define DSPONLY 0x8000 // Opcode is for the DSP Only
#define CHECK_COMMA if(*tok++ != ',') { error(comma_error); return(ERROR); }
#define CHECK_COMMA if (*tok++ != ',') { return error(comma_error); }
// Globals, externals etc
extern unsigned orgactive;

12
rmac.c
View File

@ -40,6 +40,7 @@ int debug; // [1..9] Enable debugging levels
int err_flag; // '-e' specified
int err_fd; // File to write error messages to
int rgpu, rdsp; // Assembling Jaguar GPU or DSP code
int robjproc; // Assembling Jaguar Object Processor code
int dsp56001; // Assembling DSP 56001 code
int list_fd; // File to write listing to
int regbank; // RISC register bank
@ -55,6 +56,16 @@ int activecpu = CPU_68000; // Active 68k CPU (68000 by default)
int activefpu = FPU_NONE; // Active FPU (none by default)
//
// Convert a string to uppercase
//
void strtoupper(char * s)
{
while (*s)
*s++ &= 0xDF;
}
//
// Manipulate file extension.
//
@ -271,6 +282,7 @@ int Process(int argc, char ** argv)
err_flag = 0; // Initialise error flag
rgpu = 0; // Initialise GPU assembly flag
rdsp = 0; // Initialise DSP assembly flag
robjproc = 0; // Initialise OP assembly flag
lsym_flag = 1; // Include local symbols in object file
regbank = BANK_N; // No RISC register bank specified
orgactive = 0; // Not in RISC org section

2
rmac.h
View File

@ -284,6 +284,7 @@ enum
extern int verb_flag;
extern int debug;
extern int rgpu, rdsp;
extern int robjproc;
extern int dsp56001;
extern int err_flag;
extern int err_fd;
@ -306,6 +307,7 @@ extern int activecpu;
extern int activefpu;
// Exported functions
void strtoupper(char * s);
char * fext(char *, char *, int);
int nthpath(char *, int, char *);
int ParseOptimization(char * optstring);

45
sect.c
View File

@ -290,7 +290,7 @@ int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
DEBUG { printf("AddFixup: sno=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", cursect, fixup->lineno, fixup->attr, fixup->loc, (void *)fixup->expr, (void *)fixup->symbol, fixup->orgaddr);
if (symbol != NULL)
printf(" name: %s, value: $lX\n", symbol->sname, symbol->svalue);
printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
}
return 0;
@ -679,9 +679,8 @@ int ResolveFixups(int sno)
break;
// Fixup LONG forward references;
// the long could be unaligned in the section buffer, so be careful
// (again).
// Fixup LONG forward references; the long could be unaligned in the
// section buffer, so be careful (again).
case FU_LONG:
flags = MLONG;
@ -702,6 +701,44 @@ int ResolveFixups(int sno)
SETBE32(locp, 0, eval);
break;
// Fixup QUAD forward references
// Need to add flags for OP uses... [DONE]
case FU_QUAD:
// If the symbol is undefined, make sure to pass the symbol in
// to the MarkRelocatable() function.
/* if (!(eattr & DEFINED))
MarkRelocatable(sno, loc, 0, MQUAD, esym);
else if (tdb)
MarkRelocatable(sno, loc, tdb, MQUAD, NULL);//*/
if (w & FU_OBJLINK)
{
uint64_t quad = GETBE64(locp, 0);
uint64_t addr = eval;
if (fup->orgaddr)
addr = fup->orgaddr;
//printf("sect.c: FU_OBJLINK quad=%016lX, addr=%016lX ", quad, addr);
eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
//printf("(%016lX)\n", eval);
}
else if (w & FU_OBJDATA)
{
uint64_t quad = GETBE64(locp, 0);
uint64_t addr = eval;
if (fup->orgaddr)
addr = fup->orgaddr;
eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
}
SETBE64(locp, 0, eval);
//printf("(%016lX)\n", eval);
break;
// Fixup a 3-bit "QUICK" reference in bits 9..1
// (range of 1..8) in a word. [Really bits 1..3 in a byte.]
case FU_QUICK:

12
sect.h
View File

@ -76,6 +76,7 @@
#define FU_6BRA 0x0007 // Fixup 6502-format branch offset
#define FU_BYTEH 0x0008 // Fixup 6502 high byte of immediate word
#define FU_BYTEL 0x0009 // Fixup 6502 low byte of immediate word
#define FU_QUAD 0x000A // Fixup quad-word (8 bytes)
#define FU_SEXT 0x0010 // Ok to sign extend
#define FU_PCREL 0x0020 // Subtract PC first
@ -98,9 +99,14 @@
#define FU_DONE 0x8000 // Fixup has been done
// FPU fixups
#define FU_FLOATSING 0x000A // Fixup 32-bit float
#define FU_FLOATDOUB 0x000B // Fixup 64-bit float
#define FU_FLOATEXT 0x000C // Fixup 96-bit float
#define FU_FLOATSING 0x000B // Fixup 32-bit float
#define FU_FLOATDOUB 0x000C // Fixup 64-bit float
#define FU_FLOATEXT 0x000D // Fixup 96-bit float
// OP fixups
#define FU_OBJLINK 0x10000 // Fixup OL link addr (bits 24-42, drop last 3)
#define FU_OBJDATA 0x20000 // Fixup OL data addr (bits 43-63, drop last 3)
// Chunks are used to hold generated code and fixup records
#define CHUNK struct _chunk

View File

@ -14,8 +14,8 @@
// Release Information
#define MAJOR 1 // Major version number
#define MINOR 12 // Minor version number
#define PATCH 5 // Patch release number
#define MINOR 13 // Minor version number
#define PATCH 0 // Patch release number
#endif // __VERSION_H__