The keyboard is a bit glitchy, but these are the same glitch as those that were already present in Z80 Collapse OS. They still need fixing...pull/102/head
@@ -18,7 +18,6 @@ This recipe is for installing a minimal Collapse OS system on the SMS. There | |||
are other recipes related to the SMS: | |||
* [Interfacing a PS/2 keyboard](kbd/README.md) | |||
* [zasm and ed from ROM](romasm/README.md) | |||
## Gathering parts | |||
@@ -1,69 +0,0 @@ | |||
; 8K of onboard RAM | |||
.equ RAMSTART 0xc000 | |||
; Memory register at the end of RAM. Must not overwrite | |||
.equ RAMEND 0xfdd0 | |||
jp init | |||
.fill 0x66-$ | |||
retn | |||
.inc "err.h" | |||
.inc "ascii.h" | |||
.inc "core.asm" | |||
.inc "str.asm" | |||
.equ PAD_RAMSTART RAMSTART | |||
.inc "sms/pad.asm" | |||
.inc "sms/vdp.asm" | |||
.equ GRID_RAMSTART PAD_RAMEND | |||
.equ GRID_COLS VDP_COLS | |||
.equ GRID_ROWS VDP_ROWS | |||
.equ GRID_SETCELL vdpSetCell | |||
.equ GRID_GETC padGetC | |||
.inc "grid.asm" | |||
.equ STDIO_RAMSTART GRID_RAMEND | |||
.equ STDIO_GETC gridGetC | |||
.equ STDIO_PUTC gridPutC | |||
.inc "stdio.asm" | |||
; *** BASIC *** | |||
; RAM space used in different routines for short term processing. | |||
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE | |||
.equ SCRATCHPAD STDIO_RAMEND | |||
.inc "lib/util.asm" | |||
.inc "lib/ari.asm" | |||
.inc "lib/parse.asm" | |||
.inc "lib/fmt.asm" | |||
.equ EXPR_PARSE parseLiteralOrVar | |||
.inc "lib/expr.asm" | |||
.inc "basic/util.asm" | |||
.inc "basic/parse.asm" | |||
.inc "basic/tok.asm" | |||
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE | |||
.inc "basic/var.asm" | |||
.equ BUF_RAMSTART VAR_RAMEND | |||
.inc "basic/buf.asm" | |||
.equ BAS_RAMSTART BUF_RAMEND | |||
.inc "basic/main.asm" | |||
init: | |||
di | |||
im 1 | |||
ld sp, RAMEND | |||
call gridInit | |||
call padInit | |||
call vdpInit | |||
call basInit | |||
jp basStart | |||
FNT_DATA: | |||
.bin "fnt/7x7.bin" | |||
.fill 0x7ff0-$ | |||
.db "TMR SEGA", 0x00, 0x00, 0xfb, 0x68, 0x00, 0x00, 0x00, 0x4c |
@@ -1,22 +0,0 @@ | |||
PROGNAME = ps2ctl | |||
AVRDUDEMCU ?= t45 | |||
AVRDUDEARGS ?= -c usbtiny -P usb | |||
TARGETS = $(PROGNAME).bin | |||
BASEDIR = ../../.. | |||
EDIR = $(BASEDIR)/emul | |||
# Rules | |||
.PHONY: send all clean | |||
all: $(TARGETS) | |||
@echo Done! | |||
send: $(PROGNAME).bin | |||
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$(PROGNAME).bin | |||
$(PROGNAME).bin: $(PROGNAME).fs | |||
cd $(EDIR) && ./avra.sh < ../recipes/sms/kbd/$(PROGNAME).fs > ../recipes/sms/kbd/$@ | |||
clean: | |||
rm -f $(TARGETS) |
@@ -100,16 +100,16 @@ The code expects a SR-latch that works like a 4043, that is, S and R are | |||
triggered high, S makes Q high, R makes Q low. R is hooked to PB4. S is hooked | |||
to TH (and also the A/B on the '157). Q is hooked to PB0 and TL. | |||
## Usage | |||
## Building the binary | |||
The code in this recipe is set up to listen to the keyboard on port B, leaving | |||
port A to drive, for example, an Everdrive with a D-pad. Unlike the generic | |||
SMS recipe, this kernel has no character selection mechanism. It acts like a | |||
regular shell, taking input from the keyboard. | |||
We start with the base recipe and add a few things: | |||
`kernel/sms/kbd.asm` also has a FetchKC implementation for port A if you prefer. | |||
Just hook it on. I've tried it, it works. | |||
1. at the top: `RAMSTART 0x72 + CONSTANT PS2_MEM` | |||
2. After VDP load: `641 LOAD : (ps2kc) (ps2kcB) ;` (that binds us to port B) | |||
3. Right after: `411 414 LOADR` (that gives us `(key)`) | |||
4. After `VDP$`: `PS2$`. | |||
Did you get there? Feels pretty cool huh? | |||
Rebuild, send to SMS, then run with your keyboard interface plugged to PortB. | |||
It should mostly work. There are still a few glitches to iron out... | |||
[rc2014-ps2]: ../../rc2014/ps2 |
@@ -1,82 +0,0 @@ | |||
; 8K of onboard RAM | |||
.equ RAMSTART 0xc000 | |||
; Memory register at the end of RAM. Must not overwrite | |||
.equ RAMEND 0xddd0 | |||
jp init | |||
.fill 0x66-$ | |||
retn | |||
.inc "err.h" | |||
.inc "ascii.h" | |||
.inc "core.asm" | |||
.inc "str.asm" | |||
.inc "sms/kbd.asm" | |||
.equ KBD_RAMSTART RAMSTART | |||
.equ KBD_FETCHKC smskbdFetchKCB | |||
.inc "kbd.asm" | |||
.inc "sms/vdp.asm" | |||
.equ GRID_RAMSTART KBD_RAMEND | |||
.equ GRID_COLS VDP_COLS | |||
.equ GRID_ROWS VDP_ROWS | |||
.equ GRID_SETCELL vdpSetCell | |||
.equ GRID_GETC kbdGetC | |||
.inc "grid.asm" | |||
.equ STDIO_RAMSTART GRID_RAMEND | |||
.equ STDIO_GETC gridGetC | |||
.equ STDIO_PUTC gridPutC | |||
.inc "stdio.asm" | |||
; *** BASIC *** | |||
; RAM space used in different routines for short term processing. | |||
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE | |||
.equ SCRATCHPAD STDIO_RAMEND | |||
.inc "lib/util.asm" | |||
.inc "lib/ari.asm" | |||
.inc "lib/parse.asm" | |||
.inc "lib/fmt.asm" | |||
.equ EXPR_PARSE parseLiteralOrVar | |||
.inc "lib/expr.asm" | |||
.inc "basic/util.asm" | |||
.inc "basic/parse.asm" | |||
.inc "basic/tok.asm" | |||
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE | |||
.inc "basic/var.asm" | |||
.equ BUF_RAMSTART VAR_RAMEND | |||
.inc "basic/buf.asm" | |||
.equ BAS_RAMSTART BUF_RAMEND | |||
.inc "basic/main.asm" | |||
init: | |||
di | |||
im 1 | |||
ld sp, RAMEND | |||
; Initialize the keyboard latch by "dummy reading" once. This ensures | |||
; that the adapter knows it can fill its '164. | |||
; Port B TH output, high | |||
ld a, 0b11110111 | |||
out (0x3f), a | |||
nop | |||
; Port A/B reset | |||
ld a, 0xff | |||
out (0x3f), a | |||
call kbdInit | |||
call gridInit | |||
call vdpInit | |||
call basInit | |||
jp basStart | |||
FNT_DATA: | |||
.bin "fnt/7x7.bin" | |||
.fill 0x7ff0-$ | |||
.db "TMR SEGA", 0x00, 0x00, 0xfb, 0x68, 0x00, 0x00, 0x00, 0x4c | |||
@@ -1,348 +0,0 @@ | |||
; Receives keystrokes from PS/2 keyboard and send them to the '164. On the PS/2 | |||
; side, it works the same way as the controller in the rc2014/ps2 recipe. | |||
; However, in this case, what we have on the other side isn't a z80 bus, it's | |||
; the one of the two controller ports of the SMS through a DB9 connector. | |||
; The PS/2 related code is copied from rc2014/ps2 without much change. The only | |||
; differences are that it pushes its data to a '164 instead of a '595 and that | |||
; it synchronizes with the SMS with a SR latch, so we don't need PCINT. We can | |||
; also afford to run at 1MHz instead of 8. | |||
; *** Register Usage *** | |||
; | |||
; GPIOR0 flags: | |||
; 0 - when set, indicates that the DATA pin was high when we received a | |||
; bit through INT0. When we receive a bit, we set flag T to indicate | |||
; it. | |||
; | |||
; R16: tmp stuff | |||
; R17: recv buffer. Whenever we receive a bit, we push it in there. | |||
; R18: recv step: | |||
; - 0: idle | |||
; - 1: receiving data | |||
; - 2: awaiting parity bit | |||
; - 3: awaiting stop bit | |||
; R19: Register used for parity computations and tmp value in some other places | |||
; R20: data being sent to the '164 | |||
; Y: pointer to the memory location where the next scan code from ps/2 will be | |||
; written. | |||
; Z: pointer to the next scan code to push to the 595 | |||
; | |||
.inc "avr.h" | |||
.inc "tn254585.h" | |||
.inc "tn45.h" | |||
; *** Constants *** | |||
.equ CLK 2 ; Port B | |||
.equ DATA 1 ; Port B | |||
.equ CP 3 ; Port B | |||
; SR-Latch's Q pin | |||
.equ LQ 0 ; Port B | |||
; SR-Latch's R pin | |||
.equ LR 4 ; Port B | |||
; init value for TCNT0 so that overflow occurs in 100us | |||
.equ TIMER_INITVAL 0x100-100 | |||
; *** Code *** | |||
rjmp main | |||
rjmp hdlINT0 | |||
; Read DATA and set GPIOR0/0 if high. Then, set flag T. | |||
; no SREG fiddling because no SREG-modifying instruction | |||
hdlINT0: | |||
sbic PINB, DATA ; DATA clear? skip next | |||
sbi GPIOR0, 0 | |||
set | |||
reti | |||
main: | |||
ldi r16, RAMEND&0xff | |||
out SPL, r16 | |||
ldi r16, RAMEND}8 | |||
out SPH, r16 | |||
; init variables | |||
clr r18 | |||
out GPIOR0, r18 | |||
; Setup int0 | |||
; INT0, falling edge | |||
ldi r16, 0x02 ; ISC01 | |||
out MCUCR, r16 | |||
; Enable INT0 | |||
ldi r16, 0x40 ; INT0 | |||
out GIMSK, r16 | |||
; Setup buffer | |||
clr YH | |||
ldi YL, SRAM_START&0xff | |||
clr ZH | |||
ldi ZL, SRAM_START&0xff | |||
; Setup timer. We use the timer to clear up "processbit" registers after | |||
; 100us without a clock. This allows us to start the next frame in a | |||
; fresh state. at 1MHZ, no prescaling is necessary. Each TCNT0 tick is | |||
; already 1us long. | |||
ldi r16, 0x01 ; CS00 - no prescaler | |||
out TCCR0B, r16 | |||
; init DDRB | |||
sbi DDRB, CP | |||
cbi PORTB, LR | |||
sbi DDRB, LR | |||
sei | |||
loop: | |||
brts processbit ; flag T set? we have a bit to process | |||
cp YL, ZL ; if YL == ZL, buffer is empty | |||
brne sendTo164 ; YL != ZL? our buffer has data | |||
; nothing to do. Before looping, let's check if our communication timer | |||
; overflowed. | |||
in r16, TIFR | |||
sbrc r16, 1 ; TOV0 | |||
rjmp processbitReset ; Timer0 overflow? reset processbit | |||
; Nothing to do for real. | |||
rjmp loop | |||
; Process the data bit received in INT0 handler. | |||
processbit: | |||
in r19, GPIOR0 ; backup GPIOR0 before we reset T | |||
andi r19, 0x1 ; only keep the first flag | |||
cbi GPIOR0, 0 | |||
clt ; ready to receive another bit | |||
; We've received a bit. reset timer | |||
rcall resetTimer | |||
; Which step are we at? | |||
tst r18 | |||
breq processbits0 | |||
cpi r18, 1 | |||
breq processbits1 | |||
cpi r18, 2 | |||
breq processbits2 | |||
; step 3: stop bit | |||
clr r18 ; happens in all cases | |||
; DATA has to be set | |||
tst r19 ; Was DATA set? | |||
breq loop ; not set? error, don't push to buffer | |||
; push r17 to the buffer | |||
st Y+, r17 | |||
rcall checkBoundsY | |||
rjmp loop | |||
processbits0: | |||
; step 0 - start bit | |||
; DATA has to be cleared | |||
tst r19 ; Was DATA set? | |||
brne loop ; Set? error. no need to do anything. keep r18 | |||
; as-is. | |||
; DATA is cleared. prepare r17 and r18 for step 1 | |||
inc r18 | |||
ldi r17, 0x80 | |||
rjmp loop | |||
processbits1: | |||
; step 1 - receive bit | |||
; We're about to rotate the carry flag into r17. Let's set it first | |||
; depending on whether DATA is set. | |||
clc | |||
sbrc r19, 0 ; skip if DATA cleared. | |||
sec | |||
; Carry flag is set | |||
ror r17 | |||
; Good. now, are we finished rotating? If carry flag is set, it means | |||
; that we've rotated in 8 bits. | |||
brcc loop ; we haven't finished yet | |||
; We're finished, go to step 2 | |||
inc r18 | |||
rjmp loop | |||
processbits2: | |||
; step 2 - parity bit | |||
mov r1, r19 | |||
mov r19, r17 | |||
rcall checkParity ; --> r16 | |||
cp r1, r16 | |||
brne processbitError ; r1 != r16? wrong parity | |||
inc r18 | |||
rjmp loop | |||
processbitError: | |||
clr r18 | |||
ldi r19, 0xfe | |||
rcall sendToPS2 | |||
rjmp loop | |||
processbitReset: | |||
clr r18 | |||
rcall resetTimer | |||
rjmp loop | |||
; Send the value of r20 to the '164 | |||
sendTo164: | |||
sbis PINB, LQ ; LQ is set? we can send the next byte | |||
rjmp loop ; Even if we have something in the buffer, we | |||
; can't: the SMS hasn't read our previous | |||
; buffer yet. | |||
; We disable any interrupt handling during this routine. Whatever it | |||
; is, it has no meaning to us at this point in time and processing it | |||
; might mess things up. | |||
cli | |||
sbi DDRB, DATA | |||
ld r20, Z+ | |||
rcall checkBoundsZ | |||
ldi r16, 8 | |||
sendTo164Loop: | |||
cbi PORTB, DATA | |||
sbrc r20, 7 ; if leftmost bit isn't cleared, set DATA high | |||
sbi PORTB, DATA | |||
; toggle CP | |||
cbi PORTB, CP | |||
lsl r20 | |||
sbi PORTB, CP | |||
dec r16 | |||
brne sendTo164Loop ; not zero yet? loop | |||
; release PS/2 | |||
cbi DDRB, DATA | |||
sei | |||
; Reset the latch to indicate that the next number is ready | |||
sbi PORTB, LR | |||
cbi PORTB, LR | |||
rjmp loop | |||
resetTimer: | |||
ldi r16, TIMER_INITVAL | |||
out TCNT0, r16 | |||
ldi r16, 0x02 ; TOV0 | |||
out TIFR, r16 | |||
ret | |||
; Send the value of r19 to the PS/2 keyboard | |||
sendToPS2: | |||
cli | |||
; First, indicate our request to send by holding both Clock low for | |||
; 100us, then pull Data low | |||
; lines low for 100us. | |||
cbi PORTB, CLK | |||
sbi DDRB, CLK | |||
rcall resetTimer | |||
; Wait until the timer overflows | |||
in r16, TIFR | |||
sbrs r16, 1 ; TOV0 | |||
rjmp $-4 | |||
; Good, 100us passed. | |||
; Pull Data low, that's our start bit. | |||
cbi PORTB, DATA | |||
sbi DDRB, DATA | |||
; Now, let's release the clock. At the next raising edge, we'll be | |||
; expected to have set up our first bit (LSB). We set up when CLK is | |||
; low. | |||
cbi DDRB, CLK ; Should be starting high now. | |||
; We will do the next loop 8 times | |||
ldi r16, 8 | |||
; Let's remember initial r19 for parity | |||
mov r1, r19 | |||
sendToPS2Loop: | |||
; Wait for CLK to go low | |||
sbic PINB, CLK | |||
rjmp $-2 | |||
; set up DATA | |||
cbi PORTB, DATA | |||
sbrc r19, 0 ; skip if LSB is clear | |||
sbi PORTB, DATA | |||
lsr r19 | |||
; Wait for CLK to go high | |||
sbis PINB, CLK | |||
rjmp $-2 | |||
dec r16 | |||
brne sendToPS2Loop ; not zero? loop | |||
; Data was sent, CLK is high. Let's send parity | |||
mov r19, r1 ; recall saved value | |||
rcall checkParity ; --> r16 | |||
; Wait for CLK to go low | |||
sbic PINB, CLK | |||
rjmp $-2 | |||
; set parity bit | |||
cbi PORTB, DATA | |||
sbrc r16, 0 ; parity bit in r16 | |||
sbi PORTB, DATA | |||
; Wait for CLK to go high | |||
sbis PINB, CLK | |||
rjmp $-2 | |||
; Wait for CLK to go low | |||
sbic PINB, CLK | |||
rjmp $-2 | |||
; We can now release the DATA line | |||
cbi DDRB, DATA | |||
; Wait for DATA to go low. That's our ACK | |||
sbic PINB, DATA | |||
rjmp $-2 | |||
; Wait for CLK to go low | |||
sbic PINB, CLK | |||
rjmp $-2 | |||
; We're finished! Enable INT0, reset timer, everything back to normal! | |||
rcall resetTimer | |||
clt ; also, make sure T isn't mistakely set. | |||
sei | |||
ret | |||
; Check that Y is within bounds, reset to SRAM_START if not. | |||
checkBoundsY: | |||
tst YL | |||
breq $+4 | |||
ret ; not zero, nothing to do | |||
; YL is zero. Reset Y | |||
clr YH | |||
ldi YL, SRAM_START&0xff | |||
ret | |||
; Check that Z is within bounds, reset to SRAM_START if not. | |||
checkBoundsZ: | |||
tst ZL | |||
breq $+4 | |||
ret ; not zero, nothing to do | |||
; ZL is zero. Reset Z | |||
clr ZH | |||
ldi ZL, SRAM_START&0xff | |||
ret | |||
; Counts the number of 1s in r19 and set r16 to 1 if there's an even number of | |||
; 1s, 0 if they're odd. | |||
checkParity: | |||
ldi r16, 1 | |||
lsr r19 | |||
brcc $+4 ; Carry unset? skip next | |||
inc r16 ; Carry set? We had a 1 | |||
tst r19 ; is r19 zero yet? | |||
brne checkParity+2 ; no? loop and skip first LDI | |||
andi r16, 0x1 ; Sets Z accordingly | |||
ret | |||
@@ -1 +0,0 @@ | |||
user.h |
@@ -1,21 +0,0 @@ | |||
BASEDIR = ../../.. | |||
ZASM = $(BASEDIR)/emul/zasm/zasm | |||
KERNEL = $(BASEDIR)/kernel | |||
APPS = $(BASEDIR)/apps | |||
.PHONY: all clean | |||
all: os.sms | |||
# -o value synced with offset in glue.asm | |||
ed.bin: $(APPS)/ed/glue.asm | |||
$(ZASM) -o 1f $(KERNEL) $(APPS) user.h < $(APPS)/ed/glue.asm > $@ | |||
# -o value synced with offset in glue.asm | |||
zasm.bin: $(APPS)/zasm/glue.asm | |||
$(ZASM) -o 24 $(KERNEL) $(APPS) user.h < $(APPS)/zasm/glue.asm > $@ | |||
os.sms: glue.asm ed.bin zasm.bin | |||
$(ZASM) $(KERNEL) $(APPS) ed.bin zasm.bin < glue.asm > $@ | |||
clean: | |||
rm -f os.sms ed.bin zasm.bin |
@@ -1,61 +0,0 @@ | |||
# zasm and ed from ROM | |||
SMS' RAM is much tighter than in the RC2014, which makes the idea of loading | |||
apps like zasm and ed in memory before using it a bit wasteful. In this recipe, | |||
we'll include zasm and ed code directly in the kernel and expose them as shell | |||
commands. | |||
Moreover, we'll carve ourselves a little 1K memory map to put a filesystem in | |||
there. This will give us a nice little system that can edit small source files | |||
compile them and run them. | |||
## Gathering parts | |||
* A SMS that can run Collapse OS. | |||
* A [PS/2 keyboard adapter](../kbd/README.md) | |||
## Build | |||
There's nothing special with building this recipe. Like the base recipe, run | |||
`make` then copy `os.sms` to your destination medium. | |||
If you look at the makefile, however, you'll see that we use a new trick here: | |||
we embed "apps" binaries directly in our ROM so that we don't have to load them | |||
in memory. | |||
## Usage | |||
Alright, here's what we'll do: we'll author a source file, assemble it and run | |||
it, *all* on your SMS! Commands: | |||
Collapse OS | |||
> fnew 1 src | |||
> ed src | |||
: 1i | |||
.org 0xc200 | |||
: 1a | |||
ld hl, sFoo | |||
: 2a | |||
call 0x3f | |||
: 3a | |||
xor a | |||
: 4a | |||
ret | |||
: 5a | |||
sFoo: .db "foo", 0 | |||
: w | |||
> fnew 1 dest | |||
> fopn 0 src | |||
> fopn 1 dest | |||
> zasm 1 2 | |||
First pass | |||
Second pass | |||
> dest | |||
foo> | |||
Awesome right? Some precisions: | |||
* Our glue code specifies a `USER_RAMSTART` of `0xc200`. This is where | |||
`dest` is loaded by the `pgm` shell hook. | |||
* `0x3f` is the offset of `printstr` in the jump table of our glue code. | |||
* `xor a` is for the command to report as successful to the shell. |
@@ -1,187 +0,0 @@ | |||
; TODO: This recipe has not been tested since its conversion to the BASIC shell. | |||
; My PS/2 adapter has been acting up and probably has a loose wire. I need to | |||
; fix it beore I can test this recipe on real hardware. | |||
; But theoretically, it works... | |||
; 8K of onboard RAM | |||
.equ RAMSTART 0xc000 | |||
.equ USER_CODE 0xd500 | |||
; Memory register at the end of RAM. Must not overwrite | |||
.equ RAMEND 0xddd0 | |||
jp init | |||
; *** JUMP TABLE *** | |||
jp strncmp | |||
jp upcase | |||
jp findchar | |||
jp parseHex | |||
jp blkSel | |||
jp blkSet | |||
jp fsFindFN | |||
jp fsOpen | |||
jp fsGetB | |||
jp fsPutB | |||
jp fsSetSize | |||
jp printstr | |||
jp _blkGetB | |||
jp _blkPutB | |||
jp _blkSeek | |||
jp _blkTell | |||
jp printcrlf | |||
jp stdioPutC | |||
jp stdioReadLine | |||
.fill 0x66-$ | |||
retn | |||
.inc "err.h" | |||
.inc "ascii.h" | |||
.inc "blkdev.h" | |||
.inc "fs.h" | |||
.inc "core.asm" | |||
.inc "str.asm" | |||
.inc "sms/kbd.asm" | |||
.equ KBD_RAMSTART RAMSTART | |||
.equ KBD_FETCHKC smskbdFetchKCB | |||
.inc "kbd.asm" | |||
.inc "sms/vdp.asm" | |||
.equ GRID_RAMSTART KBD_RAMEND | |||
.equ GRID_COLS VDP_COLS | |||
.equ GRID_ROWS VDP_ROWS | |||
.equ GRID_SETCELL vdpSetCell | |||
.equ GRID_GETC kbdGetC | |||
.inc "grid.asm" | |||
.equ STDIO_RAMSTART GRID_RAMEND | |||
.equ STDIO_GETC gridGetC | |||
.equ STDIO_PUTC gridPutC | |||
.inc "stdio.asm" | |||
.equ MMAP_START 0xd700 | |||
; 0x180 is to leave some space for the stack | |||
.equ MMAP_LEN RAMEND-MMAP_START-0x180 | |||
.inc "mmap.asm" | |||
.equ BLOCKDEV_RAMSTART STDIO_RAMEND | |||
.equ BLOCKDEV_COUNT 3 | |||
.inc "blockdev.asm" | |||
; List of devices | |||
.dw mmapGetB, mmapPutB | |||
.dw f0GetB, f0PutB | |||
.dw f1GetB, f1PutB | |||
.equ FS_RAMSTART BLOCKDEV_RAMEND | |||
.equ FS_HANDLE_COUNT 2 | |||
.inc "fs.asm" | |||
; *** BASIC *** | |||
; RAM space used in different routines for short term processing. | |||
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE | |||
.equ SCRATCHPAD FS_RAMEND | |||
.inc "lib/util.asm" | |||
.inc "lib/ari.asm" | |||
.inc "lib/parse.asm" | |||
.inc "lib/fmt.asm" | |||
.equ EXPR_PARSE parseLiteralOrVar | |||
.inc "lib/expr.asm" | |||
.inc "basic/util.asm" | |||
.inc "basic/parse.asm" | |||
.inc "basic/tok.asm" | |||
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE | |||
.inc "basic/var.asm" | |||
.equ BUF_RAMSTART VAR_RAMEND | |||
.inc "basic/buf.asm" | |||
.equ BFS_RAMSTART BUF_RAMEND | |||
.inc "basic/fs.asm" | |||
.inc "basic/blk.asm" | |||
.equ BAS_RAMSTART BFS_RAMEND | |||
.inc "basic/main.asm" | |||
; USER_CODE is set according to this output below. | |||
.out BAS_RAMEND | |||
init: | |||
di | |||
im 1 | |||
ld sp, RAMEND | |||
; init a FS in mmap | |||
ld hl, MMAP_START | |||
ld a, 'C' | |||
ld (hl), a | |||
inc hl | |||
ld a, 'F' | |||
ld (hl), a | |||
inc hl | |||
ld a, 'S' | |||
ld (hl), a | |||
call fsInit | |||
xor a | |||
ld de, BLOCKDEV_SEL | |||
call blkSel | |||
call fsOn | |||
call kbdInit | |||
call gridInit | |||
call vdpInit | |||
call basInit | |||
ld hl, basFindCmdExtra | |||
ld (BAS_FINDHOOK), hl | |||
jp basStart | |||
basFindCmdExtra: | |||
ld hl, basFSCmds | |||
call basFindCmd | |||
ret z | |||
ld hl, basBLKCmds | |||
call basFindCmd | |||
ret z | |||
ld hl, .mycmds | |||
call basFindCmd | |||
ret z | |||
jp basPgmHook | |||
.mycmds: | |||
.db "ed", 0 | |||
.dw 0x1f00 | |||
.db "zasm", 0 | |||
.dw 0x2400 | |||
.db 0xff | |||
f0GetB: | |||
ld ix, FS_HANDLES | |||
jp fsGetB | |||
f0PutB: | |||
ld ix, FS_HANDLES | |||
jp fsPutB | |||
f1GetB: | |||
ld ix, FS_HANDLES+FS_HANDLE_SIZE | |||
jp fsGetB | |||
f1PutB: | |||
ld ix, FS_HANDLES+FS_HANDLE_SIZE | |||
jp fsPutB | |||
; last time I checked, PC at this point was 0x128f. Let's give us a nice margin | |||
; for the start of ed. | |||
.fill 0x1f00-$ | |||
.bin "ed.bin" | |||
; Last check: 0x23b0 | |||
.fill 0x2400-$ | |||
.bin "zasm.bin" | |||
FNT_DATA: | |||
.bin "fnt/7x7.bin" | |||
.fill 0x7ff0-$ | |||
.db "TMR SEGA", 0x00, 0x00, 0xfb, 0x68, 0x00, 0x00, 0x00, 0x4c |
@@ -1,32 +0,0 @@ | |||
.equ USER_CODE 0xc200 | |||
; Make ed fit in SMS's memory | |||
.equ ED_BUF_MAXLINES 0x100 | |||
.equ ED_BUF_PADMAXLEN 0x800 | |||
; Make zasm fit in SMS's memory | |||
.equ ZASM_REG_MAXCNT 0x80 | |||
.equ ZASM_LREG_MAXCNT 0x10 | |||
.equ ZASM_REG_BUFSZ 0x800 | |||
.equ ZASM_LREG_BUFSZ 0x100 | |||
; *** JUMP TABLE *** | |||
.equ strncmp 0x03 | |||
.equ upcase @+3 | |||
.equ findchar @+3 | |||
.equ parseHex @+3 | |||
.equ blkSel @+3 | |||
.equ blkSet @+3 | |||
.equ fsFindFN @+3 | |||
.equ fsOpen @+3 | |||
.equ fsGetB @+3 | |||
.equ fsPutB @+3 | |||
.equ fsSetSize @+3 | |||
.equ printstr @+3 | |||
.equ _blkGetB @+3 | |||
.equ _blkPutB @+3 | |||
.equ _blkSeek @+3 | |||
.equ _blkTell @+3 | |||
.equ printcrlf @+3 | |||
.equ stdioPutC @+3 | |||
.equ stdioReadLine @+3 | |||